package org.jsola.hr.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.jsola.admin.query.HeaderFieldsQuery;
import org.jsola.admin.vo.HeaderFieldsListVO;
import org.jsola.admin.vo.HeaderFieldsVO;
import org.jsola.common.DateKit8;
import org.jsola.common.PageKit;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.*;
import org.jsola.hr.constant.*;
import org.jsola.hr.dao.IEmpSocInfoDAO;
import org.jsola.hr.dao.IEmpSocItemDAO;
import org.jsola.hr.dao.IEmpSocMonthDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.query.EmpSocInfoQuery;
import org.jsola.hr.query.HeaderFieldsSocQuery;
import org.jsola.hr.query.SocPlanDispatchUserQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.service.excel.EmpSocImportListener;
import org.jsola.hr.vo.*;
import org.jsola.orm.filter.Order;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import javax.validation.Valid;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.EmpSocStatus.*;
import static org.jsola.hr.constant.HrConstants.*;
import static org.jsola.hr.constant.SocPayCostType.*;

/**
 * 员工参保信息表
 *
 * @author zhr
 */
@Service("hrEmpSocInfoServiceImpl")
public class EmpSocInfoServiceImpl implements IEmpSocInfoService {

    @Autowired
    private IEmpSocInfoDAO empSocInfoDAO;

    @Autowired
    private IEmpSocItemDAO empSocItemDAO;

    @Autowired
    private IEmpSocItemService empSocItemService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocTransformService empSocTransformService;

    @Autowired
    private ISocProgItemService socProgItemService;

    @Autowired
    private ISocProgService socProgService;

    @Autowired
    private ICostBillService costBillService;

    @Autowired
    private IEmpSocPlanItemService empSocPlanItemService;

    @Autowired
    private IEmpSocRelatedService empSocRelatedService;

    @Autowired
    private IEmpSocPlanService empSocPlanService;

    @Autowired
    private IEmpSocMonthDAO empSocMonthDAO;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private ISocDifferenceService socDifferenceService;

    @Override
    public List<EmpSocInfoDO> selectBySocProgIds(List<String> socProgIds) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and().andEqualTo("valid", true).andIn("progId", socProgIds);
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public EmpSocInfoAddBatchDTO amount(EmpSocInfoAddBatchDTO empSocInfoAddBatchDTO) {
        List<EmpSocInfoAddDTO> empSocInfoAddDTOList = empSocInfoAddBatchDTO.getEmpSocInfoAddDTOList();
        for (EmpSocInfoAddDTO info : empSocInfoAddDTOList) {
            List<EmpSocItemDTO> empSocItemDTOList = info.getEmpSocItemDTOList();
            BigDecimal personalSoc = new BigDecimal("0");
            BigDecimal companySoc = new BigDecimal("0");
            BigDecimal personalPf = new BigDecimal("0");
            BigDecimal companyPf = new BigDecimal("0");
            BigDecimal personalMe = new BigDecimal("0");
            BigDecimal companyMe = new BigDecimal("0");
            for (EmpSocItemDTO bean : empSocItemDTOList) {
                BigDecimal cardinalityDefault = bean.getCardinalityDefault();
                BigDecimal company;
                BigDecimal personal;
                if (info.getOnlySocAmount() != null && info.getOnlySocAmount()) {
                    bean.setCardinalityDefault(bean.getCardinalityDefaultAmount());
                    company = bean.getAmountCompany();
                    personal = bean.getAmountPersonal();
                } else {
                    company = cardinalityDefault.multiply(bean.getPersentCompany().multiply(new BigDecimal("0.01")));
                    personal = cardinalityDefault.multiply(bean.getPersentPersonal().multiply(new BigDecimal("0.01")));
                }
                empSocTransformService.mantissaProcessing(company, bean.getMantissaProcCompany());
                empSocTransformService.mantissaProcessing(personal, bean.getMantissaProcPersonal());
                bean.setAmountCompany(company);
                bean.setAmountPersonal(personal);
                if (SOC_ITEM_TYPE.equals(bean.getItemType())) {
                    personalSoc = personalSoc.add(personal);
                    companySoc = companySoc.add(company);
                } else if (PF_ITEM_TYPE.equals(bean.getItemType())) {
                    personalPf = personalPf.add(personal);
                    companyPf = companyPf.add(company);
                } else if (ME_ITEM_TYPE.equals(bean.getItemType())) {
                    personalMe = personalMe.add(personal);
                    companyMe = companyMe.add(company);
                } else {
                    throw new ParamException("社保子项类型错误");
                }
            }
            info.setPersonalPfAmount(personalPf);
            info.setPersonalSocAmount(personalSoc);
            info.setCompanyPfAmount(companyPf);
            info.setCompanySocAmount(companySoc);
        }
        return empSocInfoAddBatchDTO;
    }

    private void calculationAmount(EmpSocInfoDO empSocInfoDO, List<EmpSocItemDO> insertItemList,
                                   Boolean onlySocAmount, TokenUser tokenUser) {
        BigDecimal personalSoc = new BigDecimal(0);
        BigDecimal personalPf = new BigDecimal(0);
        BigDecimal companySoc = new BigDecimal(0);
        BigDecimal companyPf = new BigDecimal(0);
        for (EmpSocItemDO empSocItemDO : insertItemList) {
            BigDecimal cardinalityDefault = empSocItemDO.getCardinalityDefault();
            BigDecimal company;
            BigDecimal personal;
            if (onlySocAmount) {
                company = empSocItemDO.getAmountCompany();
                personal = empSocItemDO.getAmountPersonal();
            } else {
                company = cardinalityDefault.multiply(empSocItemDO.getPersentCompany().multiply(new BigDecimal("0.01")));
                personal = cardinalityDefault.multiply(empSocItemDO.getPersentPersonal().multiply(new BigDecimal("0.01")));
            }
            company = empSocTransformService.mantissaProcessing(company, empSocItemDO.getMantissaProcCompany());
            personal = empSocTransformService.mantissaProcessing(personal, empSocItemDO.getMantissaProcPersonal());
            if (SOC_ITEM_TYPE.equals(empSocItemDO.getItemType()) ||
                    ME_ITEM_TYPE.equals(empSocItemDO.getItemType())) {
                personalSoc = personalSoc.add(personal);
                companySoc = companySoc.add(company);
            } else {
                personalPf = personalPf.add(personal);
                companyPf = companyPf.add(company);
            }
        }
        // 如果四个金额都相等直接返回就可以
        if (empSocInfoDO.getPersonalSocAmount().compareTo(personalSoc) == 0 &&
                empSocInfoDO.getPersonalPfAmount().compareTo(personalPf) == 0 &&
                empSocInfoDO.getCompanySocAmount().compareTo(companySoc) == 0 &&
                empSocInfoDO.getCompanyPfAmount().compareTo(companyPf) == 0) {
            return;
        }
        empSocInfoDO.setPersonalPfAmount(personalPf);
        empSocInfoDO.setPersonalSocAmount(personalSoc);
        empSocInfoDO.setCompanyPfAmount(companyPf);
        empSocInfoDO.setCompanySocAmount(companySoc);
        updateByIdSelective(empSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(EmpSocInfoUpdateDTO empSocInfoUpdateDTO, TokenUser tokenUser) {
        //转DO
        EmpSocInfoDO empSocInfoDO = empSocInfoUpdateDTO.to(EmpSocInfoDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(empSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... empSocInfoIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) empSocInfoIds);
    }


    @Override
    public EmpSocInfoVO selectById(String empSocInfoId, String siteId) {
        EmpSocInfoDO empSocInfoDO = selectDOById(empSocInfoId, siteId);
        if (empSocInfoDO == null) {
            return null;
        }
        return empSocInfoDO.to(EmpSocInfoVO.class);
    }

    @Override
    public List<EmpSocInfoListVO> select(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        List<EmpSocInfoDO> empSocInfoDOList = selectDO(empSocInfoQuery, siteId);
        if (CollectionUtils.isEmpty(empSocInfoDOList)) {
            return empSocInfoDOList == null ? null : new ArrayList<>();
        }
        return empSocInfoDOList.stream()
                .map(empSocInfoDO -> empSocInfoDO.to(EmpSocInfoListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        Example example = buildExample(empSocInfoQuery, siteId);
        return empSocInfoDAO.selectCountByExample(example);
    }

    @Override
    public Page<EmpSocInfoListVO> selectPage(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        Example example = buildExample(empSocInfoQuery, siteId);
        Page<EmpSocInfoDO> page = empSocInfoDAO.selectPageByExample(example,
                empSocInfoQuery.getPageNo(),
                empSocInfoQuery.getPageSize());

        return page.to(EmpSocInfoListVO.class);
    }

    @Override
    public EmpSocInfoDO selectDOById(String empSocInfoId, String siteId) {
        return listById(empSocInfoId, siteId);
    }

    @Override
    public List<EmpSocInfoDO> selectDO(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        Example example = buildExample(empSocInfoQuery, siteId);
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public BulkEditWrongsVO bulkEditImport(File file, String monthId, TokenUser tokenUser) {
        EmpSocImportListener empSocImportListener = new EmpSocImportListener();
        empSocImportListener.setTokenUser(tokenUser);
        empSocImportListener.setMonthId(monthId);
        EasyExcel.read(file, empSocImportListener).headRowNumber(2).sheet().doRead();
        if (!CollectionUtils.isEmpty(empSocImportListener.getRepeatHead())) {
            String repeatHead = String.join(",", empSocImportListener.getRepeatHead());
            throw new ParamException("表头" + repeatHead + "重复,请检查表头后重新上传");
        }
        return empSocImportListener.getBulkEditWrongsVO();
    }

    @Override
    public void bulkEdit(List<BulkEditCheckWrongsDTO> successList, TokenUser tokenUser) {
        List<String> schemeIdList = successList.stream()
                .map(checkWrongs -> checkWrongs.getEmpSocInfoDO().getProgId())
                .collect(Collectors.toList());
        List<SocProgDO> schemeList = socProgService.selectByIdList(schemeIdList, tokenUser);
        Map<String, SocProgDO> schemeMap = schemeList.stream()
                .collect(Collectors.toMap(SocProgDO::getId, v -> v));
        // 循环excel里面的数据
        for (BulkEditCheckWrongsDTO importData : successList) {
            EmpSocInfoDO empSocInfoDO = importData.getEmpSocInfoDO();
            List<EmpSocItemDO> empSocItemList = importData.getEmpSocItemList();
            Map<String, String> dataMap = importData.getDataMap();
            // 更新子项里面的基数
            SocProgDO socProgDO = schemeMap.get(empSocInfoDO.getProgId());
            empSocItemService.updateItemCardinality(dataMap, empSocItemList, socProgDO.getOnlySocAmount(), tokenUser);
            String serviceCost = dataMap.get("服务费");
            if (!StringUtils.isEmpty(serviceCost)) {
                empSocInfoDO.setServiceCost(new BigDecimal(serviceCost));
                updateByIdSelective(empSocInfoDO);
            }
            calculationAmount(empSocInfoDO, empSocItemList, socProgDO.getOnlySocAmount(), tokenUser);
        }
    }

    @Override
    public String exportErrorListPath(BulkEditWrongsVO bulkEditWrongsVO, TokenUser tokenUser) {
        List<List<String>> dataList = new ArrayList<>();
        List<List<String>> headList = new ArrayList<>();
        List<String> allHead = bulkEditWrongsVO.getHead();
        List<Map<String, String>> errorList = bulkEditWrongsVO.getErrorList();
        for (String oneHead : allHead) {
            List<String> head = new ArrayList<>();
            head.add(oneHead);
            headList.add(head);
        }
        for (Map<String, String> errorMap : errorList) {
            List<String> oneDate = new ArrayList<>();
            for (String head : allHead) {
                oneDate.add(errorMap.get(head));
            }
            dataList.add(oneDate);
        }
        return ExcelKit.writeExcelWithModel("参保数据错误数据表", dataList, headList, false,
                "错误数据", (CellWriteHandler) null);
    }

    /**
     * 根据查询参数，构建example
     *
     * @param empSocInfoQuery 查询参数
     * @param siteId          所属站点id
     * @return example
     */
    private Example buildExample(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (empSocInfoQuery.getSupplementType() != null) {
            example.and().andLike("supplementType", "%" + empSocInfoQuery.getSupplementType() + "%");
        }
        if (empSocInfoQuery.getEmpId() != null) {
            example.and().andEqualTo("empId", empSocInfoQuery.getEmpId());
        }
        if (empSocInfoQuery.getProgId() != null) {
            example.and().andEqualTo("progId", empSocInfoQuery.getProgId());
        }
        if (empSocInfoQuery.getMonthId() != null) {
            example.and().andEqualTo("monthId", empSocInfoQuery.getMonthId());
        }
        if (empSocInfoQuery.getYear() != null) {
            example.and().andEqualTo("year", empSocInfoQuery.getYear());
        }
        if (empSocInfoQuery.getMonth() != null) {
            example.and().andEqualTo("month", empSocInfoQuery.getMonth());
        }
        if (empSocInfoQuery.getPersonalSocAmount() != null) {
            example.and().andEqualTo("personalSocAmount", empSocInfoQuery.getPersonalSocAmount());
        }
        if (empSocInfoQuery.getPersonalPfAmount() != null) {
            example.and().andEqualTo("personalPfAmount", empSocInfoQuery.getPersonalPfAmount());
        }
        if (empSocInfoQuery.getCompanySocAmount() != null) {
            example.and().andEqualTo("companySocAmount", empSocInfoQuery.getCompanySocAmount());
        }
        if (empSocInfoQuery.getCompanyPfAmount() != null) {
            example.and().andEqualTo("companyPfAmount", empSocInfoQuery.getCompanyPfAmount());
        }
        if (empSocInfoQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", empSocInfoQuery.getCompanyId());
        }
        if (empSocInfoQuery.getGroupId() != null) {
            example.and().andEqualTo("groupId", empSocInfoQuery.getGroupId());
        }
        if (empSocInfoQuery.getEmpSocStatus() != null) {
            example.and().andEqualTo("empSocStatus", empSocInfoQuery.getEmpSocStatus());
        }
        // 排序
        ExampleKit.setExampleOrder(example, empSocInfoQuery.getOrders());
        return example;
    }

    @Override
    public List<EmpSocInfoListVO> getEmpSocInfoMonthList(EmpSocInfoQuery empSocInfoQuery, TokenUser tokenUser) {
        EmpSocMonthDO empSocMonthDO = empSocMonthService.checkLastMonth(empSocInfoQuery.getCompanyId(), tokenUser.getSiteId());
        List<EmpSocInfoListVO> empSocInfoListVOList = empSocInfoDAO.getEmpSocInfoMonthList(empSocInfoQuery, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(empSocInfoListVOList)) {
            return empSocInfoListVOList == null ? null : new ArrayList<>();
        } else {
            for (EmpSocInfoListVO empSocInfoListVO : empSocInfoListVOList) {
                // 计算额外统计项
                monthListStatistics(empSocInfoListVO, empSocInfoQuery, tokenUser.getSiteId());
                empSocInfoListVO.setCanNotEdited(costBillService.checkMonthIdCreateCostBill(empSocInfoListVO.getId(), tokenUser.getSiteId()));
                if (!ObjectUtils.isEmpty(empSocMonthDO)) {
                    empSocInfoListVO.setMonthWhetherCurrent(empSocMonthDO.getYear().equals(empSocInfoListVO.getYear()) &&
                            empSocMonthDO.getMonth().equals(empSocInfoListVO.getMonth()));
                } else {
                    empSocInfoListVO.setMonthWhetherCurrent(false);
                }
            }
        }
        return empSocInfoListVOList;
    }


    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String saveEmpSocInfoNextMonth(EmpSocInfoQuery empSocInfoQuery, TokenUser tokenUser) {
        //验证日期是否正确
        this.dateVerification(empSocInfoQuery.getYear(), empSocInfoQuery.getMonth());
        //验证当前月份之后只能有两个账单
        String monthId = verificationData(empSocInfoQuery.getYear(), empSocInfoQuery.getMonth(), empSocInfoQuery.getCompanyId(), tokenUser);
        if (!StringUtils.isEmpty(monthId)) {
            // 创建次月账单需要把上个月的信息延续到下个月去,所以说需要先查前一个月的参保管理信息
            // 也就是说刚才查到的那个monthId的前一个月的参保信息记录
            EmpSocMonthDO empSocMonth = empSocMonthService.selectDOById(monthId, tokenUser.getSiteId());
            Date date = DateKit8.parseDate(empSocMonth.getYear() + "-" + empSocMonth.getMonth() + "-01");
            // 查询前一个月的MonthId
            Date previousDate = DateKit8.addMonths(date, -1L);
            String[] previousDateArray = DateUtils.getYearAndMonth(previousDate);
            String previousMonthId = empSocMonthService.selectMonthIdByYearAndMonth(empSocInfoQuery.getCompanyId(),
                    previousDateArray[0], previousDateArray[1], tokenUser.getSiteId());
            // 查询前一个月的参保信息,被删除的员工的记录也在这,下面添加次月账单信息的时候需要把被删除的员工的记录给去掉
            List<EmpSocInfoListVO> empSocInfoList = empSocInfoDAO.selectInfoByMonthId2(previousMonthId,
                    empSocInfoQuery.getCompanyId(), tokenUser.getSiteId());
            if (CollectionUtils.isEmpty(empSocInfoList)) {
                updateMonthId(empSocMonth, tokenUser);
                return "";
            }
            List<String> empIdList = empSocInfoList.stream().map(EmpSocInfoListVO::getEmpId).collect(Collectors.toList());
            List<String> socInfoId = empSocInfoList.stream().map(EmpSocInfoListVO::getId).collect(Collectors.toList());
            List<EmpSocItemVO> empSocItemList = empSocItemService.selectByEmpSocIdList(socInfoId, tokenUser);
            Map<String, List<EmpSocItemVO>> groupItem = empSocItemList.stream()
                    .collect(Collectors.groupingBy(EmpSocItemVO::getEmpSocId));
            List<EmpSocInfoDO> insertList = new ArrayList<>();
            List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService.selectByEmpIdList(empIdList, tokenUser.getSiteId());
            Map<String, EmpSocRelatedDO> empSocRelatedMap =
                    empSocRelatedList.stream().collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
            // 这里查询的是没有被删除的员工
            List<EmpInfoDO> empInfoList = empInfoService.selectDoByIds(empIdList);
            List<String> empIdListFilter = empInfoList.stream().map(EmpInfoDO::getId).collect(Collectors.toList());
            Map<String, EmpInfoDO> empMap = empInfoList.stream().collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
            Map<String, List<EmpSocItemVO>> itemMap = new HashMap<>();
            for (EmpSocInfoListVO previousInfo : empSocInfoList) {
                EmpSocRelatedDO empSocRelatedDO = empSocRelatedMap.get(previousInfo.getEmpId());
                if (!empIdListFilter.contains(previousInfo.getEmpId())
                        || checkEmpStopSocDate(empSocRelatedDO, previousInfo, tokenUser)) {
                    continue;
                }
                // 根据员工的离职日期判断
                EmpInfoDO empInfo = empMap.get(previousInfo.getEmpId());
                Date leaveDate = empInfo.getLeaveDate();
                EmpSocInfoDO empSocInfoDO = new EmpSocInfoDO();
                BeanUtils.copyProperties(previousInfo, empSocInfoDO);
                checkNextEmpSocStatus(empSocInfoDO, previousInfo);
                empSocInfoDO.setId(UUID.randomUUID().toString().replace("-", "").toUpperCase());
                itemMap.put(empSocInfoDO.getId(), groupItem.get(previousInfo.getId()));
                empSocInfoDO.preInsert(tokenUser.getUserId());
                empSocInfoDO.setSiteId(tokenUser.getSiteId());
                empSocInfoDO.setYear(empSocMonth.getYear());
                empSocInfoDO.setMonth(empSocMonth.getMonth());
                empSocInfoDO.setMonthId(empSocMonth.getId());
                if (!ObjectUtils.isEmpty(leaveDate) && empInfo.getStatus() == 4) {
                    // 判断一下离职时间
                    String leaveDateStr = DateUtils.getYearMonthStr(leaveDate);
                    if (Integer.parseInt(leaveDateStr) <= Integer.parseInt(empSocMonth.getYear() + empSocMonth.getMonth())) {
                        empSocInfoDO.setWhetherLeave(true);
                    }
                } else {
                    empSocInfoDO.setWhetherLeave(false);
                }
                insertList.add(empSocInfoDO);
            }
            if (!CollectionUtils.isEmpty(insertList)) {
                empSocInfoDAO.insertListAndSetId(insertList);
                List<String> schemeIdList = insertList.stream()
                        .map(EmpSocInfoDO::getProgId).collect(Collectors.toList());
                List<SocProgDO> schemeList = socProgService.selectByIdList(schemeIdList, tokenUser);
                Map<String, SocProgDO> schemeMap = schemeList.stream().collect(Collectors.toMap(SocProgDO::getId, v -> v));
                createSocItemByNextMonth(insertList, empSocRelatedMap, itemMap, empSocMonth, schemeMap, tokenUser);
            }
            updateMonthId(empSocMonth, tokenUser);
            // log日志
            HrLogKit.logForSave("由管理员" + tokenUser.getUserName() + "创建社保次月账单 创建月份为" +
                            empSocMonth.getYear() + "-" + empSocMonth.getMonth(),
                    "EmpSocInfo", empSocInfoQuery.getCompanyId(), empSocInfoQuery.getCompanyId(), tokenUser);
            return "";
        } else {
            return "已有两个新账单，不能继续添加";
        }
    }

    @Override
    public void updateMonthId(EmpSocMonthDO empSocMonthDO, TokenUser tokenUser) {
        List<EmpSocPlanDO> socPlanList = empSocPlanService.selectSuccessFeedbackByMonth(empSocMonthDO, tokenUser);
        if (CollectionUtils.isEmpty(socPlanList)) {
            return;
        }
        List<String> empIdList = socPlanList.stream().map(EmpSocPlanDO::getEmpId)
                .distinct().collect(Collectors.toList());
        List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService.selectByEmpIdList(empIdList, tokenUser.getSiteId());
        Map<String, EmpSocRelatedDO> empSocRelatedMap =
                empSocRelatedList.stream().collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
        Map<String, List<EmpSocPlanDO>> planGroup = socPlanList.stream()
                .collect(Collectors.groupingBy(EmpSocPlanDO::getEmpId));
        for (String empId : empIdList) {
            List<EmpSocPlanDO> empSocPlanList = planGroup.get(empId);
            EmpSocInfoDO empSocInfoDO = selectEmpSocInfoByEmpIdAndMonthIdNormal
                    (empSocMonthDO.getId(), empId, empSocMonthDO.getCompanyId(), tokenUser);
            if (ObjectUtils.isEmpty(empSocInfoDO)) {
                EmpSocPlanDO socPlan = empSocPlanList.get(0);
                empSocInfoDO = socPlan.to(EmpSocInfoDO.class);
                empSocInfoDO.setId(null);
                empSocInfoDO.setProgId(socPlan.getSchemeId());
                empSocInfoDO.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getValue());
                empSocInfoDO.setPlanFlag(true);
                empSocInfoDO.setCompanySocAmount(new BigDecimal("0"));
                empSocInfoDO.setPersonalSocAmount(new BigDecimal("0"));
                empSocInfoDO.setPersonalPfAmount(new BigDecimal("0"));
                empSocInfoDO.setCompanyPfAmount(new BigDecimal("0"));
                empSocInfoDO.setMonthId(empSocMonthDO.getId());
            }
            List<EmpSocItemDO> insetEmpSocItem = new ArrayList<>();
            for (EmpSocPlanDO empSocPlanDO : empSocPlanList) {
                Date planDate = DateKit8.parseDate(empSocPlanDO.getYear() + "-" + empSocPlanDO.getMonth() + "-" + "01");
                EmpSocRelatedDO empSocRelated = empSocRelatedMap.get(empSocPlanDO.getEmpId());
                if (EmpSocStatus.INCREMENT.getValue().equals(empSocPlanDO.getEmpSocState()) ||
                        EmpSocStatus.WAIT_INCREMENT.getValue().equals(empSocPlanDO.getEmpSocState())) {
                    // 如果这个是增员计划,预存子项
                    List<EmpSocPlanItemDO> planItemList = empSocPlanItemService.selectByPlanId(empSocPlanDO.getId(), tokenUser);
                    insetEmpSocItem.addAll(planItemList.stream().map(empSocPlanItemDO -> {
                        EmpSocItemDO empSocItemDO = empSocPlanItemDO.to(EmpSocItemDO.class);
                        empSocItemDO.setId(null);
                        empSocItemDO.setItemType(empSocPlanDO.getType());
                        return empSocItemDO;
                    }).collect(Collectors.toList()));
                    empSocInfoDO.setProgId(empSocPlanDO.getSchemeId());
                    if (SOC_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                        empSocRelated.setSocState(INCREMENT.getValue());
                        empSocRelated.setSocIncreaseDate(planDate);
                        empSocInfoDO.setEmpSocStatus(EmpSocStatus.INCREMENT.getValue());
                    } else if (ME_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                        empSocRelated.setMeState(INCREMENT.getValue());
                        empSocRelated.setSocIncreaseDate(planDate);
                        empSocInfoDO.setEmpMeStatus(EmpSocStatus.INCREMENT.getValue());
                    } else {
                        empSocRelated.setPfState(INCREMENT.getValue());
                        empSocRelated.setSocIncreaseDate(planDate);
                        empSocInfoDO.setEmpPfStatus(EmpSocStatus.INCREMENT.getValue());
                    }
                } else if (EmpSocStatus.ATTRITION.getValue().equals(empSocPlanDO.getEmpSocState()) ||
                        EmpSocStatus.WAIT_ATTRITION.getValue().equals(empSocPlanDO.getEmpSocState())) {
                    // 如果这个是减员计划,去掉相关子项
                    // 把对应的状态改为减员状态,去掉预存集合中对应的子项,或者删除本来存在的子项
                    if (SOC_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                        empSocRelated.setSocState(ATTRITION.getValue());
                        empSocRelated.setSocStopDate(planDate);
                        empSocInfoDO.setEmpSocStatus(EmpSocStatus.ATTRITION.getValue());
                    } else if (ME_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                        empSocRelated.setMeState(ATTRITION.getValue());
                        empSocRelated.setMeStopDate(planDate);
                        empSocInfoDO.setEmpMeStatus(EmpSocStatus.ATTRITION.getValue());
                    } else {
                        empSocRelated.setPfState(ATTRITION.getValue());
                        empSocRelated.setPfSealedDate(planDate);
                        empSocInfoDO.setEmpPfStatus(EmpSocStatus.ATTRITION.getValue());
                    }
                    if (!CollectionUtils.isEmpty(insetEmpSocItem)) {
                        insetEmpSocItem.removeIf(item -> empSocPlanDO.getType().equals(item.getItemType()));
                    }
                    if (!StringUtils.isEmpty(empSocInfoDO.getId())) {
                        empSocItemService.deleteByEmpSocInfoIdAndItemType(empSocInfoDO.getId(),
                                empSocPlanDO.getType(), tokenUser.getSiteId());
                    }
                    // 赋值服务费
                    if (!ObjectUtils.isEmpty(empSocPlanDO.getServiceCost())) {
                        empSocInfoDO.setServiceCost(empSocPlanDO.getServiceCost());
                    }
                }
            }
            empSocRelatedService.asyncUpdateRelated(new ArrayList<>(empSocRelatedMap.values()));
            String empSocInfoId = empSocInfoDO.getId();
            if (StringUtils.isEmpty(empSocInfoId)) {
                empSocInfoDO = save(empSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
            }
            if (!CollectionUtils.isEmpty(insetEmpSocItem)) {
                for (EmpSocItemDO empSocItemDO : insetEmpSocItem) {
                    empSocItemDO.setEmpSocId(empSocInfoDO.getId());
                }
                empSocItemDAO.insertListAndSetId(insetEmpSocItem);
            }
            List<EmpSocItemDO> itemList = empSocItemService.selectByEmpSocId(empSocInfoDO.getId(), tokenUser.getSiteId());
            SocProgDO socProgDO = socProgService.selectDOById(empSocInfoDO.getProgId(), tokenUser.getSiteId());
            calculationAmount(empSocInfoDO, itemList, socProgDO.getOnlySocAmount(), tokenUser);
        }
    }

    /**
     * 这里传过来的年月一直是当月(也就是当前月)
     * 这个方法会校验当前月有没有创建参保月,没有则找最近一个月的下一个月创建,并返回Id
     * 有就查下个月,下个月如果没有则创建下个月返回下个月的Id
     * 如果下个月还有,再查下下个月,没有创建,返回Id
     * 要是下下个月还有,就报错不能再往下创建了
     */
    private String verificationData(String year, String month, String companyId, TokenUser tokenUser) {
        // 当月转成Date格式,方便后面加月份
        Date date = DateKit8.parseDate(year + "-" + month + "-01");
        String currentMonthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, year, month, tokenUser.getSiteId());
        if (StringUtils.isEmpty(currentMonthId)) {
            EmpSocMonthDO currentMaxMonth = empSocMonthDAO.selectMaxDate(companyId, tokenUser.getSiteId());
            Date currentMaxDate = DateKit8.parseDate(currentMaxMonth.getYear() + "-"
                    + currentMaxMonth.getMonth() + "-01");
            Date currentMaxDateNext = DateKit8.addMonths(currentMaxDate, 1L);
            String[] currentMaxDateNextStr = DateUtils.getYearAndMonth(currentMaxDateNext);
            return empSocMonthService.saveByYearAndMonth(companyId, currentMaxDateNextStr[0],
                    currentMaxDateNextStr[1], tokenUser).getId();
        }
        Date nextDate = DateKit8.addMonths(date, 1L);
        String[] nextDateStr = DateUtils.getYearAndMonth(nextDate);
        String nextMonthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, nextDateStr[0],
                nextDateStr[1], tokenUser.getSiteId());
        if (StringUtils.isEmpty(nextMonthId)) {
            return empSocMonthService.saveByYearAndMonth(companyId, nextDateStr[0], nextDateStr[1], tokenUser).getId();
        }
        Date nextTwoDate = DateKit8.addMonths(nextDate, 1L);
        String[] nextTwoDateStr = DateUtils.getYearAndMonth(nextTwoDate);
        String nextTwoMonthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, nextTwoDateStr[0],
                nextTwoDateStr[1], tokenUser.getSiteId());
        if (StringUtils.isEmpty(nextTwoMonthId)) {
            return empSocMonthService.saveByYearAndMonth(companyId, nextTwoDateStr[0], nextTwoDateStr[1], tokenUser).getId();
        }
        return null;
    }

    private void dateVerification(String year, String month) {
        if (StringUtils.isEmpty(year) || StringUtils.isEmpty(month)) {
            throw new HrException("年或月不能为空");
        }
    }

    @Override
    public Page<EmpSocInfoListVO> selectPageEmpSocInfo(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        Page<EmpSocInfoListVO> resultPage = selectSocPage(empSocInfoQuery, siteId);
        if (CollectionUtils.isEmpty(resultPage.getEntities())) {
            return resultPage;
        }
        List<EmpSocInfoListVO> resultList = resultPage.getEntities();
        List<String> infoIdList = resultList.stream().map(EmpSocInfoListVO::getId).collect(Collectors.toList());
        // 填充差额费用和参保子项
        Map<String, List<SocDifferenceDO>> differenceMap = new HashMap<>(16);
        List<SocDifferenceDO> socDifferenceList = socDifferenceService.selectBySocInfoIdList(infoIdList, siteId);
        Map<String, List<EmpSocItemDO>> itemMap = new HashMap<>(256);
        List<EmpSocItemDO> empSocItemList = empSocItemService.selectByEmpSocIdList(infoIdList, siteId);
        if (!CollectionUtils.isEmpty(socDifferenceList)) {
            differenceMap = socDifferenceList
                    .stream().collect(Collectors.groupingBy(SocDifferenceDO::getSocInfoId));
        }
        if (!CollectionUtils.isEmpty(empSocItemList)) {
            itemMap = empSocItemList.stream()
                    .collect(Collectors.groupingBy(EmpSocItemDO::getEmpSocId));
        }
        for (EmpSocInfoListVO resultInfo : resultList) {
            List<SocDifferenceDO> socDifferences = differenceMap.get(resultInfo.getId());
            if (!CollectionUtils.isEmpty(socDifferences)) {
                Map<String, BigDecimal> socDifferenceMap = new HashMap<>(8);
                for (SocDifferenceDO socDifference : socDifferences) {
                    if (!ObjectUtils.isEmpty(socDifference.getPersonalDifference()) &&
                            socDifference.getPersonalDifference().compareTo(BigDecimal.ZERO) != 0) {
                        socDifferenceMap.put(socDifference.getDifferenceName() + "(个人)",
                                socDifference.getPersonalDifference());
                    }
                    if (!ObjectUtils.isEmpty(socDifference.getCompanyDifference()) &&
                            socDifference.getCompanyDifference().compareTo(BigDecimal.ZERO) != 0) {
                        socDifferenceMap.put(socDifference.getDifferenceName() + "(单位)",
                                socDifference.getCompanyDifference());
                    }
                }
                resultInfo.setDifferenceHead(socDifferenceMap);
            }
            List<EmpSocItemDO> empSocItem = itemMap.get(resultInfo.getId());
            if (!CollectionUtils.isEmpty(empSocItem)) {
                List<SocProgItemListVO> itemList = empSocItem.stream().map(item -> {
                    SocProgItemListVO itemVO = item.to(SocProgItemListVO.class);
                    itemVO.setAmountCount(itemVO.getAmountCompany().add(itemVO.getAmountPersonal()));
                    return itemVO;
                }).collect(Collectors.toList());
                resultInfo.setProgItemListVo(itemList);
                resultInfo.getProgItemListVo().add(this.countEmpItem(resultInfo));
            } else {
                resultInfo.setProgItemListVo(new ArrayList<>());
            }
        }
        return resultPage;
    }

    @Override
    public Page<EmpSocInfoListVO> selectPageEmpSocBack(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        List<EmpSocInfoListVO> returnEmpSocInfoList = new ArrayList<>();
        String monthId = empSocMonthService
                .selectMonthIdByYearAndMonth(empSocInfoQuery.getCompanyId(),
                        empSocInfoQuery.getYear(),
                        empSocInfoQuery.getMonth(),
                        siteId);
        if (StringUtils.isEmpty(monthId)) {
            return new Page<>(empSocInfoQuery.getPageSize(), empSocInfoQuery.getPageNo());
        }
        empSocInfoQuery.setMonthId(monthId);
        List<EmpSocInfoListVO> empSocInfoListVOList = empSocInfoDAO
                .pageEmpSocInfoList(empSocInfoQuery, siteId, null);
        List<String> empSocInfoIdList = empSocInfoListVOList.stream()
                .map(EmpSocInfoListVO::getId).collect(Collectors.toList());
        List<EmpSocItemDO> empSocItemList = empSocItemService.selectByEmpSocIdList(empSocInfoIdList, siteId);
        Map<String, List<EmpSocItemDO>> itemMap = empSocItemList.stream()
                .collect(Collectors.groupingBy(EmpSocItemDO::getEmpSocId));
        for (EmpSocInfoListVO empSocInfoListVO : empSocInfoListVOList) {
            List<EmpSocItemDO> empSocItem = itemMap.get(empSocInfoListVO.getId());
            if (!CollectionUtils.isEmpty(empSocItem)) {
                List<SocProgItemListVO> itemList = empSocItem.stream().map(item -> {
                    SocProgItemListVO itemVO = item.to(SocProgItemListVO.class);
                    itemVO.setAmountCount(itemVO.getAmountCompany().add(itemVO.getAmountPersonal()));
                    return itemVO;
                }).collect(Collectors.toList());
                empSocInfoListVO.setProgItemListVo(itemList);
            } else {
                empSocInfoListVO.setProgItemListVo(new ArrayList<>());
            }
        }
        // 根据empId进行分类
        Map<String, List<EmpSocInfoListVO>> empSocInfoListVOMap =
                empSocInfoListVOList.stream().collect(Collectors.groupingBy(EmpSocInfoListVO::getEmpId));
        for (Map.Entry<String, List<EmpSocInfoListVO>> entry : empSocInfoListVOMap.entrySet()) {
            // 构建新的返回数据
            EmpSocInfoListVO empSocInfoListVO = new EmpSocInfoListVO();
            List<SocProgItemListVO> socProgItemListVOList = new ArrayList<>();
            empSocInfoListVO.setName(entry.getValue().get(0).getName());
            empSocInfoListVO.setEmpId(entry.getKey());
            // 查询员工信息
            EmpInfoDO empInfoDO = empInfoService.selectDOByIdIgnoreValid(entry.getKey());
            empSocInfoListVO.setRemark(empInfoDO.getRemark());
            // 累加个人社保  个人公积金  公司社保  公司公积金  服务费  滞纳金
            BigDecimal personalSocAmount = new BigDecimal("0");
            BigDecimal personalPfAmount = new BigDecimal("0");
            BigDecimal companySocAmount = new BigDecimal("0");
            BigDecimal companyPfAmount = new BigDecimal("0");
            BigDecimal serviceCost = new BigDecimal("0");
            BigDecimal socialSecurityFine = new BigDecimal("0");
            BigDecimal accumulationFundFine = new BigDecimal("0");
            List<EmpSocInfoListVO> value = entry.getValue();
            for (EmpSocInfoListVO socInfoListVO : value) {
                personalSocAmount = personalSocAmount.add(socInfoListVO.getPersonalSocAmount() == null ?
                        new BigDecimal("0") : socInfoListVO.getPersonalSocAmount());
                personalPfAmount = personalPfAmount.add(socInfoListVO.getPersonalPfAmount() == null ?
                        new BigDecimal("0") : socInfoListVO.getPersonalPfAmount());
                companySocAmount = companySocAmount.add(socInfoListVO.getCompanySocAmount() == null ?
                        new BigDecimal("0") : socInfoListVO.getCompanySocAmount());
                companyPfAmount = companyPfAmount.add(socInfoListVO.getCompanyPfAmount() == null ?
                        new BigDecimal("0") : socInfoListVO.getCompanyPfAmount());
                serviceCost = serviceCost.add(socInfoListVO.getServiceCost() == null ?
                        new BigDecimal("0") : socInfoListVO.getServiceCost());
                socialSecurityFine = socialSecurityFine.add(socInfoListVO.getSocialSecurityFine() == null ?
                        new BigDecimal("0") : socInfoListVO.getSocialSecurityFine());
                accumulationFundFine = accumulationFundFine.add(socInfoListVO.getAccumulationFundFine() == null ?
                        new BigDecimal("0") : socInfoListVO.getAccumulationFundFine());
                socProgItemListVOList.addAll(socInfoListVO.getProgItemListVo());
            }
            empSocInfoListVO.setPersonalSocAmount(personalSocAmount);
            empSocInfoListVO.setPersonalPfAmount(personalPfAmount);
            empSocInfoListVO.setCompanySocAmount(companySocAmount);
            empSocInfoListVO.setCompanyPfAmount(companyPfAmount);
            empSocInfoListVO.setServiceCost(serviceCost);
            empSocInfoListVO.setSocialSecurityFine(socialSecurityFine);
            empSocInfoListVO.setAccumulationFundFine(accumulationFundFine);
            empSocInfoListVO.setProgItemListVo(socProgItemListVOList);
            returnEmpSocInfoList.add(empSocInfoListVO);
        }
        return PageKit.startPage(returnEmpSocInfoList, empSocInfoQuery.getPageNo(), empSocInfoQuery.getPageSize());
    }

    private Page<EmpSocInfoListVO> selectSocPage(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        if (!StringUtils.isEmpty(empSocInfoQuery.getTag())) {
            String[] split = empSocInfoQuery.getTag().split(",");
            empSocInfoQuery.setTagList(Arrays.asList(split));
        }
        if (ObjectUtils.isEmpty(empSocInfoQuery.getTopStatus())) {
            empSocInfoQuery.setTopStatus(-1);
        }
        List<EmpSocInfoListVO> empSocInfoList = empSocInfoDAO.pageEmpSocInfoList(empSocInfoQuery, siteId, null);
        return PageKit.startPage(empSocInfoList, empSocInfoQuery.getPageNo(), empSocInfoQuery.getPageSize());
    }

    @Override
    public List<EmpSocInfoListVO> selectEmpSocInfoByExport(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        if (!StringUtils.isEmpty(empSocInfoQuery.getTag())) {
            String[] split = empSocInfoQuery.getTag().split(",");
            empSocInfoQuery.setTagList(Arrays.asList(split));
        }
        return empSocInfoDAO
                .pageEmpSocInfoList(empSocInfoQuery, siteId, empSocInfoQuery.getEmpSocInfoIdList());
    }

    @Override
    public List<EmpSocInfoListVO> jfEmpSocInfoList(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        return empSocInfoDAO.jfEmpSocInfoList(empSocInfoQuery, siteId);
    }

    private SocProgItemListVO countEmpItem(EmpSocInfoListVO empSocInfoListVO) {
        SocProgItemListVO socProgItemListVO = new SocProgItemListVO();
        BigDecimal countCompany = new BigDecimal(0);
        BigDecimal countPersonal = new BigDecimal(0);
        BigDecimal count = new BigDecimal(0);
        for (SocProgItemListVO item : empSocInfoListVO.getProgItemListVo()) {
            countCompany = countCompany.add(item.getAmountCompany());
            countPersonal = countPersonal.add(item.getAmountPersonal());
            count = count.add(item.getAmountCount());
        }
        socProgItemListVO.setItemName("缴费合计");
        socProgItemListVO.setAmountCompany(countCompany);
        socProgItemListVO.setAmountPersonal(countPersonal);
        socProgItemListVO.setAmountCount(count);
        if (!CollectionUtils.isEmpty(empSocInfoListVO.getProgItemListVo())) {
            socProgItemListVO.setSort(empSocInfoListVO.getProgItemListVo().size());
        } else {
            socProgItemListVO.setSort(0);
        }
        socProgItemListVO.setId("1");
        return socProgItemListVO;
    }

    @Override
    public EmpSocInfoListVO getAmount(String year, String month, String companyId, String monthId, String siteId) {
        this.dateVerification(year, month);
        LocalDate dateTime = LocalDate.parse(year + "-" + month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String year_previous = dateTime.minusMonths(1).getYear() + "";
        String month_previous = dateTime.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")).substring(5, 7);
        EmpSocInfoListVO amount = empSocInfoDAO.getAmount(year, month, companyId, monthId, siteId, year_previous, month_previous);
        BigDecimal bigDecimal = new BigDecimal(0);
        bigDecimal = bigDecimal.add(amount.getPersonalSocAmount());
        bigDecimal = bigDecimal.add(amount.getPersonalPfAmount());
        bigDecimal = bigDecimal.add(amount.getCompanySocAmount());
        bigDecimal = bigDecimal.add(amount.getCompanyPfAmount());
        amount.setAmountCount(bigDecimal.setScale(2, RoundingMode.HALF_UP));
        return amount;
    }

    @Override
    public Page<EmpSocInfoListVO> selectPageAddEmpSocInfo(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        String orderBy = this.orderBy(empSocInfoQuery.getOrders());
        LocalDate dateTime = LocalDate.parse(empSocInfoQuery.getYear() + "-" + empSocInfoQuery.getMonth() + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String year_previous = dateTime.minusMonths(1).getYear() + "";
        String month_previous = dateTime.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")).substring(5, 7);
        Page<EmpSocInfoListVO> empSocInfoListVOPage = this.selectPageAdd(empSocInfoQuery, empSocInfoQuery.getPageNo(), empSocInfoQuery.getPageSize(), siteId, orderBy, year_previous, month_previous);
        return empSocInfoListVOPage.to(EmpSocInfoListVO.class);
    }

    private Page<EmpSocInfoListVO> selectPageAdd(EmpSocInfoQuery empSocInfoQuery, int pageNo, int pageSize, String siteId, String orderBy, String year_previous, String month_previous) {
        PageInfo<EmpSocInfoListVO> pageInfo =
                PageHelper.startPage(pageNo, pageSize).doSelectPageInfo(() ->
                        empSocInfoDAO.pageAddEmpSocInfoList(empSocInfoQuery, siteId, orderBy, year_previous, month_previous)
                                .forEach(empSocInfoListVO -> empSocInfoListVO.getProgItemListVo().add(this.countEmpItem(empSocInfoListVO))));
        return new Page<>(pageInfo.getList(), pageInfo.getPageNum(), pageInfo.getPageSize(), (int) pageInfo.getTotal());
    }

    private String orderBy(List<Order> orders) {
        String orderBy = "";
        if (!CollectionUtils.isEmpty(orders)) {
            String o = orders.get(0).getProperty();
            if ("join_date".equals(o)) {
                orderBy = "emp." + o;
            } else if ("name".equals(o)) {
                orderBy = "emp." + o;
            } else if ("dep_id".equals(o)) {
                orderBy = "dep." + o;
            } else if ("soc_city".equals(o)) {
                orderBy = "prog." + o;
            } else {
                orderBy = "";
            }
        }
        return orderBy;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Boolean setCardinality(TokenUser tokenUser, EmpSocItemListBatchDTO empSocItemListBatchDTO) {
        List<EmpSocItemListVO> empSocItemListVOList = empSocItemListBatchDTO.getEmpSocItemListVOList();
        ParamException.notEmpty(empSocItemListVOList, "员工参保子项不能为空");
        ParamException.notNull(empSocItemListBatchDTO.getProgId(), "员工参保方案不能为空");
        List<Integer> itemTypeList = empSocItemListVOList.stream()
                .map(EmpSocItemListVO::getItemType).distinct().collect(Collectors.toList());
        SocProgVO socScheme = socProgService.selectById(empSocItemListBatchDTO.getProgId(), tokenUser.getSiteId());
        if (!StringUtils.isEmpty(empSocItemListBatchDTO.getId())) {
            EmpSocInfoDO empSocInfoDO = selectDOById(empSocItemListBatchDTO.getId(), tokenUser.getSiteId());
            empSocItemService.deleteBySocInfoId(empSocInfoDO.getId());
            EmpSocRelatedDO empSocRelatedDO = empSocRelatedService
                    .selectByEmpId(empSocInfoDO.getEmpId(), tokenUser.getSiteId());
            if (!empSocItemListBatchDTO.getProgId().equals(empSocRelatedDO.getSchemeId())) {
                empSocRelatedDO.setSchemeId(empSocItemListBatchDTO.getProgId());
                if(itemTypeList.contains(SOC_ITEM_TYPE)){
                    empSocRelatedDO.setSocAccount(StringUtils.isEmpty(socScheme.getSocAccount()) ? "" : socScheme.getSocAccount());
                }
                if(itemTypeList.contains(ME_ITEM_TYPE)){
                    empSocRelatedDO.setMeAccount(StringUtils.isEmpty(socScheme.getMeAccount()) ? "" : socScheme.getMeAccount());
                }
                if(itemTypeList.contains(PF_ITEM_TYPE)){
                    empSocRelatedDO.setPfAccount(StringUtils.isEmpty(socScheme.getPfAccount()) ? "" : socScheme.getPfAccount());
                }
                empSocRelatedService.updateByIdSelective(empSocRelatedDO, tokenUser.getSiteId(), tokenUser.getUserId());
            }
            //个人社保
            BigDecimal ps = new BigDecimal(0);
            //个人公积金
            BigDecimal pp = new BigDecimal(0);
            //公司社保
            BigDecimal cs = new BigDecimal(0);
            //公司公积金
            BigDecimal cp = new BigDecimal(0);
            if (!CollectionUtils.isEmpty(empSocItemListVOList)) {
                List<EmpSocItemDO> empSocItemDOList = new ArrayList<>();
                for (EmpSocItemListVO bean : empSocItemListVOList) {
                    if ("缴费合计".equals(bean.getItemName())) {
                        break;
                    }
                    //基数
                    BigDecimal cardinalityDefault = bean.getCardinalityDefault();
                    BigDecimal personal;
                    BigDecimal company;
                    if (empSocItemListBatchDTO.getOnlySocAmount() != null && empSocItemListBatchDTO.getOnlySocAmount()) {
                        personal = bean.getAmountPersonal();
                        company = bean.getAmountCompany();
                    } else {
                        personal = cardinalityDefault.multiply(bean.getPersentPersonal().multiply(new BigDecimal("0.01")));
                        company = cardinalityDefault.multiply(bean.getPersentCompany().multiply(new BigDecimal("0.01")));
                    }
                    personal = empSocTransformService.mantissaProcessing(personal, bean.getMantissaProcPersonal());
                    company = empSocTransformService.mantissaProcessing(company, bean.getMantissaProcCompany());
                    if (SOC_ITEM_TYPE.equals(bean.getItemType()) || ME_ITEM_TYPE.equals(bean.getItemType())) {
                        ps = ps.add(personal);
                        cs = cs.add(company);
                    } else {
                        pp = pp.add(personal);
                        cp = cp.add(company);
                    }
                    EmpSocItemDO empSocItemDO = bean.to(EmpSocItemDO.class);
                    if (empSocItemListBatchDTO.getOnlySocAmount() != null && empSocItemListBatchDTO.getOnlySocAmount()) {
                        empSocItemDO.setCardinalityDefault(bean.getCardinalityDefaultAmount());
                    }
                    empSocItemDO.setAmountCompany(company);
                    empSocItemDO.setAmountPersonal(personal);
                    empSocItemDO.setId(null);
                    empSocItemDO.preInsert(tokenUser.getUserId());
                    empSocItemDO.setSiteId(tokenUser.getSiteId());
                    empSocItemDOList.add(empSocItemDO);
                }
                List<EmpSocInfoListVO> empSocId = new ArrayList<>();
                EmpSocInfoListVO empSocInfoListVO = new EmpSocInfoListVO();
                empSocInfoListVO.setId(empSocItemListBatchDTO.getId());
                empSocId.add(empSocInfoListVO);
                empSocInfoDAO.updateItemByProgPayProvidentFund(empSocId, new Date(), tokenUser.getUserId(), null);
                // 批量添加
                empSocItemDAO.insertListAndSetId(empSocItemDOList);
            }
            empSocInfoDO.setProgId(empSocItemListBatchDTO.getProgId());
            empSocInfoDO.setPersonalPfAmount(pp);
            empSocInfoDO.setPersonalSocAmount(ps);
            empSocInfoDO.setCompanyPfAmount(cp);
            empSocInfoDO.setCompanySocAmount(cs);
            empSocInfoDO.setServiceCost(empSocItemListBatchDTO.getServiceCost());
            empSocInfoDAO.updateByPrimaryKeySelective(empSocInfoDO);
        }
        return true;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Boolean setAmount(TokenUser tokenUser, EmpSocItemListBatchDTO empSocItemListBatchDTO) {
        @Valid List<EmpSocItemListVO> empSocItemListVOList = empSocItemListBatchDTO.getEmpSocItemListVOList();
        empSocInfoDAO.updateHrSocItemAmount(empSocItemListVOList, new Date(), tokenUser.getUserId());
        return true;
    }

    @Override
    public Page<EmpSocInfoListVO> selectEmpSocInfoByEmpId(EmpSocInfoQuery empSocInfoQuery, String siteId) {
        PageInfo<EmpSocInfoListVO> pageInfo =
                PageHelper.startPage(empSocInfoQuery.getPageNo(), empSocInfoQuery.getPageSize()).doSelectPageInfo(() ->
                        empSocInfoDAO.selectEmpSocInfoByEmpId(empSocInfoQuery, siteId)
                                .forEach(empSocInfoListVO -> empSocInfoListVO.getProgItemListVo().add(this.countEmpItem(empSocInfoListVO))));
        Page<EmpSocInfoListVO> page = new Page<>(pageInfo.getList(), pageInfo.getPageNum(), pageInfo.getPageSize(), (int) pageInfo.getTotal());
        return page.to(EmpSocInfoListVO.class);
    }

    @Override
    public List<String> selectYearByEmpId(String empId, String companyId, String siteId) {
        return empSocInfoDAO.selectYearDistinctByEmpId(empId, companyId, siteId);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Integer updateEmpSocInfoByMonthId(String[] monthId, TokenUser tokenUser) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, monthId);
        return empSocInfoDAO.updateEmpSocInfoByMonthId(list, new Date(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public Integer updateSocInfoByProgId(EmpSocInfoAddDTO empSocInfoAddDTO, List<String> time, TokenUser tokenUser) {
        List<EmpSocInfoUpdateDTO> list = this.time(time);
        return empSocInfoDAO.updateSocInfoByProgId(empSocInfoAddDTO, list, new Date(), tokenUser.getUserId());
    }

    @Override
    public List<EmpSocInfoListVO> selectInfoByProgId(String progId, String siteId, List<String> time) {
        List<EmpSocInfoListVO> listVOS = new ArrayList<>();
        for (EmpSocInfoUpdateDTO t : this.time(time)) {
            EmpSocInfoQuery empSocInfoQuery = new EmpSocInfoQuery();
            empSocInfoQuery.setProgId(progId);
            empSocInfoQuery.setYear(t.getYear());
            empSocInfoQuery.setMonth(t.getMonth());
            List<EmpSocInfoListVO> select = this.select(empSocInfoQuery, siteId);
            listVOS.addAll(select);
        }

        return listVOS;
    }

    @Override
    public Integer updateItemByProgPayProvidentFund(List<EmpSocInfoListVO> empSocInfoListVOS, TokenUser tokenUser) {
        return empSocInfoDAO.updateItemByProgPayProvidentFund(empSocInfoListVOS, new Date(), tokenUser.getUserId(), "2");
    }

    @Override
    public Integer updateItemByEmpSocId(List<EmpSocItemDO> list, TokenUser tokenUser) {
        return empSocInfoDAO.updateItemByEmpSocId(list, new Date(), tokenUser.getUserId());
    }

    private List<EmpSocInfoUpdateDTO> time(List<String> time) {
        List<EmpSocInfoUpdateDTO> list = new ArrayList<>();
        for (String t : time) {
            EmpSocInfoUpdateDTO empSocInfoUpdateDTO = new EmpSocInfoUpdateDTO();
            empSocInfoUpdateDTO.setYear(t.substring(0, 4));
            empSocInfoUpdateDTO.setMonth(t.substring(5, 7));
            list.add(empSocInfoUpdateDTO);
        }
        return list;
    }

    @Override
    public List<EmpSocInfoListVO> selectTimeInfoByProgId(String progId, String siteId) {
        return empSocInfoDAO.selectTimeInfoByProgId(progId, siteId);
    }

    @Override
    public List<EmpSocInfoDO> selectInfoByCompanyId(String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocInfoDO> selectByMonthAndEmpIds(Integer yearly, Integer monthly, List<String> empIds, String companyId, String siteId) {
        if (CollectionUtils.isEmpty(empIds)) {
            return new ArrayList<>();
        }
        String month;
        if (monthly < 10) {
            month = "0" + monthly;
        } else {
            month = monthly.toString();
        }
        String monthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, yearly.toString(), month, siteId);
        // 差额费用记录
        List<SocDifferenceDO> socDifferenceList = socDifferenceService
                .selectByMonthAndCompanyList(Collections.singletonList(companyId), Collections.singletonList(monthId), siteId);
        Map<String, List<SocDifferenceDO>> groupDifference = socDifferenceList.stream()
                .collect(Collectors.groupingBy(SocDifferenceDO::getEmpId));
        if (StringUtils.isEmpty(monthId)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("monthId", monthId)
                .andIn("empId", empIds);
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        List<EmpSocInfoDO> result =  new ArrayList<>(empSocInfoDOList.stream()
                // 表示empId为key， 接着如果有重复的，那么从EmpSocInfoDO对象o1与o2中筛选出一个，这里选择o1，
                // 并把empId重复，需要将公司社保,公司公积金,个人社保,个人公积金与o1进行合并的o2, 赋值给o1，最后返回o1
                .collect(Collectors.toMap(EmpSocInfoDO::getEmpId, a -> a, (o1, o2) -> {
                    o1.setCompanyPfAmount(o1.getCompanyPfAmount().add(o2.getCompanyPfAmount()));
                    o1.setCompanySocAmount(o1.getCompanySocAmount().add(o2.getCompanySocAmount()));
                    o1.setPersonalPfAmount(o1.getPersonalPfAmount().add(o2.getPersonalPfAmount()));
                    o1.setPersonalSocAmount(o1.getPersonalSocAmount().add(o2.getPersonalSocAmount()));
                    return o1;
                })).values());
        for (EmpSocInfoDO empSocInfo : result) {
            List<SocDifferenceDO> empDifferenceList = groupDifference.get(empSocInfo.getEmpId());
            if(!CollectionUtils.isEmpty(empDifferenceList)){
                for (SocDifferenceDO socDifference : empDifferenceList) {
                    empSocInfo.setPersonalSocAmount(empSocInfo.getPersonalSocAmount().add(socDifference.getPersonalDifference()));
                    empSocInfo.setCompanySocAmount(empSocInfo.getCompanySocAmount().add(socDifference.getCompanyDifference()));
                }
            }
        }
        return result;
    }

    @Override
    public List<EmpSocInfoVO> selectItemByMonthAndEmpIds(Integer yearly, Integer monthly, String companyId,
                                                         List<String> empIds, String siteId) {
        if (CollectionUtils.isEmpty(empIds)) {
            return new ArrayList<>();
        }
        String month;
        if (monthly < 10) {
            month = "0" + monthly;
        } else {
            month = monthly.toString();
        }
        String monthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, yearly.toString(), month, siteId);
        if (StringUtils.isEmpty(monthId)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("monthId", monthId)
                .andIn("empId", empIds)
                .andEqualTo("siteId", siteId)
                .andEqualTo("valid", true);
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        Map<String, List<EmpSocInfoDO>> empSocInfoListGroupEmpId =
                empSocInfoList.parallelStream()
                        .collect(Collectors.groupingBy(EmpSocInfoDO::getEmpId));
        //每个人的 好多参保管理条目
        List<EmpSocInfoVO> result = new ArrayList<>();
        for (String empId : empSocInfoListGroupEmpId.keySet()) {
            EmpSocInfoVO empSocInfoVO = new EmpSocInfoVO();
            // 失业 初始化0
            empSocInfoVO.setUnemploymentInsurance(new BigDecimal("0"));
            // 医疗 初始化0
            empSocInfoVO.setMedicalInsurance(new BigDecimal("0"));
            // 养老 初始化0
            empSocInfoVO.setPensionInsurance(new BigDecimal("0"));
            // 公积金 初始化0
            empSocInfoVO.setHousingFund(new BigDecimal("0"));
            empSocInfoVO.setEmpId(empId);
            List<EmpSocInfoDO> oneEmpSocInfoList = empSocInfoListGroupEmpId.get(empId);
            for (EmpSocInfoDO empSocInfoDO : oneEmpSocInfoList) {
                // 拿到这个人的 这条参保管理的所有子项
                List<EmpSocItemDO> empSocItemDOList = empSocItemService.selectEmpSocItemByInfoId(empSocInfoDO.getId(), siteId);
                // 根据参保子项名称分组
                Map<String, List<EmpSocItemDO>> collect =
                        empSocItemDOList
                                .parallelStream()
                                .collect(Collectors.groupingBy(EmpSocItemDO::getItemName));
                List<EmpSocItemDO> pensionInsurance = collect.get("养老保险");
                if (!CollectionUtils.isEmpty(pensionInsurance)) {
                    empSocInfoVO.setPensionInsurance(empSocInfoVO.getPensionInsurance().add(pensionInsurance.get(0).getAmountPersonal()));
                }
                List<EmpSocItemDO> medicalInsurance = collect.get("医疗保险");
                if (!CollectionUtils.isEmpty(medicalInsurance)) {
                    empSocInfoVO.setMedicalInsurance(empSocInfoVO.getMedicalInsurance().add(medicalInsurance.get(0).getAmountPersonal()));
                }
                List<EmpSocItemDO> unemploymentInsurance = collect.get("失业保险");
                if (!CollectionUtils.isEmpty(unemploymentInsurance)) {
                    empSocInfoVO.setUnemploymentInsurance(empSocInfoVO.getUnemploymentInsurance().add(unemploymentInsurance.get(0).getAmountPersonal()));
                }
                List<EmpSocItemDO> housingFund = collect.get("公积金");
                if (!CollectionUtils.isEmpty(housingFund)) {
                    empSocInfoVO.setHousingFund(empSocInfoVO.getHousingFund().add(housingFund.get(0).getAmountPersonal()));
                }
            }
            result.add(empSocInfoVO);
        }
        return result;
    }

    @Override
    public EmpSocInfoDO getEmpSocInfoByEmpId(String empId, String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("siteId", siteId)
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true);
        //按照创建时间倒序排序
        example.orderBy("gmtCreate").desc();
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            return null;
        }
        return empSocInfoList.get(0);
    }

    @Override
    public EmpSocInfoVO refunds(EmpSocInfoAddDTO empSocInfoAddDTO, TokenUser tokenUser) {
        String empId = empSocInfoAddDTO.getEmpId();
        String monthId = empSocInfoAddDTO.getMonthId();
        String companyId = empSocInfoAddDTO.getCompanyId();
        Integer refundsType = empSocInfoAddDTO.getRefundsType();
        EmpSocRelatedDO empSocRelated = empSocRelatedService.selectByEmpId(empId, tokenUser.getSiteId());
        List<EmpSocInfoDO> empSocInfoList =
                selectRefundsInfoByType(monthId, empId, companyId, refundsType, tokenUser);
        // 查看退费时间是否与已有退费记录时间有交集
        for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
            if (DateUtils.checkTimesHasOverlap(empSocInfoAddDTO.getRefundsBeginTime(), empSocInfoAddDTO.getRefundsEndTime(),
                    empSocInfoDO.getRefundsBeginTime(), empSocInfoDO.getRefundsEndTime())) {
                throw new ParamException("退费时间有误");
            }
        }
        List<Date> monthDifferences = DateUtils
                .getMonthDifferences(empSocInfoAddDTO.getRefundsBeginTime(), empSocInfoAddDTO.getRefundsEndTime());
        for (Date monthDifference : monthDifferences) {
            String monthDifferenceMonthId = empSocMonthService
                    .selectMonthIdByDate(companyId, monthDifference, tokenUser.getSiteId());
            if (!StringUtils.isEmpty(monthDifferenceMonthId)) {
                EmpSocMonthDO empSocMonthDO = empSocMonthService.selectDOById(monthDifferenceMonthId, tokenUser.getSiteId());
                EmpSocInfoDO empSocInfoDO = selectInfoByMonthId(empId, monthId, tokenUser);
                if (!ObjectUtils.isEmpty(empSocInfoDO)) {
                    List<EmpSocItemDO> empSocItemList = empSocItemService
                            .selectByEmpSocId(empSocInfoDO.getId(), tokenUser.getSiteId());
                    List<Integer> itemTypeList = empSocItemList.stream()
                            .map(EmpSocItemDO::getItemType).collect(Collectors.toList());
                    if (itemTypeList.contains(refundsType)) {
                        throw new ParamException("员工" + empSocMonthDO.getMonth() + "月" +
                                SchemeType.find(refundsType).getDesc() + "已生成正常参保记录无法退费");
                    }
                }
            }
        }
        empSocInfoAddDTO.setPersonalSocAmount(empSocInfoAddDTO.getPersonalSocAmount().multiply(new BigDecimal("-1")));
        empSocInfoAddDTO.setCompanySocAmount(empSocInfoAddDTO.getCompanySocAmount().multiply(new BigDecimal("-1")));
        empSocInfoAddDTO.setPersonalPfAmount(empSocInfoAddDTO.getPersonalPfAmount().multiply(new BigDecimal("-1")));
        empSocInfoAddDTO.setCompanyPfAmount(empSocInfoAddDTO.getCompanyPfAmount().multiply(new BigDecimal("-1")));
        empSocInfoAddDTO.setPayCostType(SocPayCostType.REFUNDS.getValue());
        empSocInfoAddDTO.setEmpSocStatus(empSocRelated.getSocState());
        empSocInfoAddDTO.setEmpMeStatus(empSocRelated.getMeState());
        empSocInfoAddDTO.setEmpPfStatus(empSocRelated.getPfState());
        empSocInfoAddDTO.setPlanFlag(true);
        EmpSocInfoDO save = save(empSocInfoAddDTO.to(EmpSocInfoDO.class), tokenUser.getSiteId(), tokenUser.getUserId());
        return save.to(EmpSocInfoVO.class);
    }

    @Override
    public List<EmpSocInfoDO> getEmpSocInfoByEmpIdOrderByGmtCreate(String empId, String siteId, String companyId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("companyId", companyId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        example.orderBy("gmtCreate").desc();
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public EmpSocInfoVO getEmpSocInfoWithEmpInfo(String empId, String companyId, TokenUser tokenUser) {
        EmpSocInfoVO empSocInfoVO = new EmpSocInfoVO();
        EmpInfoDO empInfoDO = empInfoService.selectDOById(empId, tokenUser.getSiteId());
        empSocInfoVO.setSocCard(empInfoDO.getSocCard());
        empSocInfoVO.setPfCard(empInfoDO.getPfCard());
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andNotIn("payCostType", Arrays.asList(FILL_DIFFERENCE.getValue(), FILL_COLLECT.getValue()))
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true);
        example.orderBy("gmtCreate");
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoDOList)) {
            return empSocInfoVO;
        } else {
            List<EmpSocItemDO> empSocItemDOList = empSocItemService.selectEmpSocItemByInfoId(empSocInfoDOList.get(0).getId(), tokenUser.getSiteId());
            List<EmpSocItemVO> collect = empSocItemDOList.parallelStream().map(empSocItemDO -> empSocItemDO.to(EmpSocItemVO.class)).collect(Collectors.toList());
            empSocInfoVO.setEmpSocItemVOList(collect);
        }
        return empSocInfoVO;
    }

    @Override
    public List<EmpSocInfoDO> getEmpSocInfoNormal(String empId, String companyId, String siteId, Boolean planFlag) {
        Example exampleSocInfo = new Example(EmpSocInfoDO.class);
        exampleSocInfo.and()
                .andEqualTo("valid", true)
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", siteId)
                .andEqualTo("companyId", companyId);
        if (planFlag != null) {
            exampleSocInfo.and()
                    .andEqualTo("planFlag", planFlag);
        }
        exampleSocInfo.orderBy("gmtCreate").desc();
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(exampleSocInfo);
        // 过滤补收补缴的情况
        return empSocInfoList.parallelStream().
                filter(empSocInfoDO -> !(FILL_DIFFERENCE.getValue().equals(empSocInfoDO.getPayCostType()) ||
                        FILL_COLLECT.getValue().equals(empSocInfoDO.getPayCostType())))
                .collect(Collectors.toList());
    }

    @Override
    public List<EmpSocInfoDO> selectEmpInfoByMonthId(String companyId, String monthId, TokenUser tokenUser) {
        Example exampleSocInfo = new Example(EmpSocInfoDO.class);
        exampleSocInfo.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("companyId", companyId)
                .andEqualTo("monthId", monthId)
                .andEqualTo("planFlag", true);
        return empSocInfoDAO.selectByExample(exampleSocInfo);
    }

    @Override
    public List<EmpSocInfoDO> selectEmpInfoByMonthIdList(List<String> monthIdList, TokenUser tokenUser) {
        Example exampleSocInfo = new Example(EmpSocInfoDO.class);
        exampleSocInfo.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("monthId", monthIdList)
                .andEqualTo("planFlag", true);
        return empSocInfoDAO.selectByExample(exampleSocInfo);
    }

    @Override
    public Map<String, Long> selectHomeSocCount(TokenUser tokenUser, String year, String month,
                                                List<String> companyIdList, String siteId) {
        List<EmpSocMonthDO> empSocMonthDOList = empSocMonthService.selectHomeMonthId(year, month, companyIdList, tokenUser);
        if (CollectionUtils.isEmpty(empSocMonthDOList)) {
            return new HashMap<>(8);
        }
        List<String> monthIdList = empSocMonthDOList.parallelStream().map(EmpSocMonthDO::getId).collect(Collectors.toList());
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("monthId", monthIdList);
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        Map<String, Long> result = new HashMap<>(8);
        long increaseCount = 0L;
        long decreaseCount = 0L;
        // 根据员工ID去重
        long socCount = 0L;
        if (!CollectionUtils.isEmpty(empSocInfoDOList)) {
            socCount += empSocInfoDOList.stream().map(EmpSocInfoDO::getEmpId).distinct().count();
            for (EmpSocInfoDO empSocInfoDO : empSocInfoDOList) {
                if (NORMAL_PAY_COST.getValue().equals(empSocInfoDO.getPayCostType())) {
                    if (INCREMENT.getValue().equals(empSocInfoDO.getEmpSocStatus()) ||
                            INCREMENT.getValue().equals(empSocInfoDO.getEmpMeStatus()) ||
                            INCREMENT.getValue().equals(empSocInfoDO.getEmpPfStatus())) {
                        increaseCount += 1;
                    }
                    if (ATTRITION.getValue().equals(empSocInfoDO.getEmpSocStatus()) ||
                            ATTRITION.getValue().equals(empSocInfoDO.getEmpMeStatus()) ||
                            ATTRITION.getValue().equals(empSocInfoDO.getEmpPfStatus())) {
                        decreaseCount += 1;
                    }
                }
            }
        }
        result.put(INCREASE_STR, increaseCount);
        result.put(DECREASE_STR, decreaseCount);
        result.put(NORMAL_SOC_STR, socCount);
        return result;
    }


    @Override
    public Boolean empIsExistIncreasePlan(String empId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("valid", true)
                .andEqualTo("empSocStatus", INCREMENT.getValue())
                .andEqualTo("planFlag", false);
        // 没有返回 true 有返回 false
        return CollectionUtils.isEmpty(empSocInfoDAO.selectByExample(example));
    }


    @Override
    public EmpSocInfoDO selectEmpSocInfoByYearMonthEmpIdPayCostTypeMonthId(String year, String month, Integer payCostType,
                                                                           String empId, String monthId, String companyId,
                                                                           String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("valid", true)
                .andEqualTo("monthId", monthId)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("payCostType", payCostType)
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", siteId);
        return empSocInfoDAO.selectOneByExample(example);
    }

    @Override
    public List<EmpSocInfoDO> selectEmpSocInfoByMonthIdAndEmpId(String empId, List<String> monthIdList, String companyId, Boolean valid, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andIn("monthId", monthIdList)
                .andEqualTo("companyId", companyId)
                .andEqualTo("planFlag", true)
                .andEqualTo("siteId", siteId);
        if (valid) {
            example.and().andEqualTo("valid", true);
        } else {
            example.and().andIsNull("valid");
        }
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoDOList)) {
            return new ArrayList<>();
        }
        return empSocInfoDOList;
    }

    @Override
    public List<String> selectEmpListWithSocProgId(String socProgId, String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("progId", socProgId)
                .andEqualTo("siteId", siteId);
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        return empSocInfoDOList.parallelStream().map(EmpSocInfoDO::getEmpId).distinct().collect(Collectors.toList());
    }

    @Override
    public String selectEmpIsHaveFillCollect(String supplementType, String year, String month, String empId, String monthId, String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andLike("supplementType", "%" + supplementType + "%")
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("payCostType", FILL_COLLECT.getValue())
                .andEqualTo("monthId", monthId)
                .andEqualTo("empId", empId)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("siteId", siteId);
        EmpSocInfoDO empSocInfoDO = empSocInfoDAO.selectOneByExample(example);
        if (ObjectUtils.isEmpty(empSocInfoDO)) {
            return "";
        }
        return empSocInfoDO.getId();
    }

    @Override
    public List<Date> selectEmpIsHaveFillCollectAndNormalDate(String empId, String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", siteId);
        example.and()
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .orEqualTo("supplementType", "1");
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            return new ArrayList<>();
        }
        return empSocInfoList.stream().
                map(empSocInfoDO -> DateKit8.parse(empSocInfoDO.getYear() + "-" +
                        empSocInfoDO.getMonth() + "-01", "yyyy-MM-dd")).distinct().collect(Collectors.toList());
    }

    @Override
    public Map<String, Long> selectIncreaseDecreaseCount(String startYear, String startMonth, String endYear,
                                                         String endMonth, String companyId, Boolean isAcrossYear, String siteId) {

        List<String> monthIdList =
                empSocMonthService.selectMonthIdForWhile(startYear, startMonth, endYear, endMonth, companyId, isAcrossYear, siteId);
        if (CollectionUtils.isEmpty(monthIdList)) {
            return new HashMap<>(8);
        }
        List<EmpSocMonthDO> empSocMonthList = empSocMonthService.selectByMonthIdList(monthIdList);
        Map<String, EmpSocMonthDO> monthMap = empSocMonthList.stream()
                .collect(Collectors.toMap(EmpSocMonthDO::getId, v -> v));
        Example example = new Example(EmpSocInfoDO.class);
        List<EmpSocInfoListVO> resultGroupList = new ArrayList<>();
        for (String monthId : monthIdList) {
            EmpSocMonthDO empSocMonthDO = monthMap.get(monthId);
            example.clear();
            example.and()
                    .andEqualTo("companyId", companyId)
                    .andEqualTo("valid", true)
                    .andEqualTo("planFlag", true)
                    .andEqualTo("monthId", monthId)
                    .andEqualTo("siteId", siteId);
            List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
            resultGroupList.addAll(empSocInfoList.stream().map(empSocInfoDO -> {
                EmpSocInfoListVO empSocInfoListVO = empSocInfoDO.to(EmpSocInfoListVO.class);
                setPersonStatus(empSocInfoListVO, empSocMonthDO.getYear(), empSocMonthDO.getMonth(), siteId);
                return empSocInfoListVO;
            }).collect(Collectors.toList()));
        }


        return resultGroupList.stream()
                .collect(Collectors.groupingBy(EmpSocInfoListVO::getPersonStatus, Collectors.counting()));
    }

    @Override
    public Page<EmpSocSmallProgramVO> userEmpSocInfoForMini(Integer pageSize, Integer pageNo, TokenUser tokenUser) {
        List<EmpSocSmallProgramVO> empSocSmallProgramList = new ArrayList<>();
        //查询员工信息
        EmpInfoDO empInfoDO = empInfoService.selectEmpByUserId(tokenUser);
        if (ObjectUtils.isEmpty(empInfoDO)) {
            return new Page<>(pageSize, pageNo);
        }
        List<EmpSocInfoDO> empSocInfoList = this.selectEmpSocInfoBrieflyByEmpId(empInfoDO.getId(),
                empInfoDO.getCompanyId(), tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            return new Page<>(pageSize, pageNo);
        }
        List<String> monthIdList = empSocInfoList.stream().map(EmpSocInfoDO::getMonthId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(monthIdList)) {
            return new Page<>(pageSize, pageNo);
        }
        List<EmpSocMonthDO> monthList = empSocMonthService.selectByMonthIdList(monthIdList);
        Map<String, EmpSocMonthDO> monthMap = monthList.stream()
                .collect(Collectors.toMap(EmpSocMonthDO::getId, v -> v));
        // 以月分组
        Map<String, List<EmpSocInfoDO>> monthIdEmpSocInfoListMap =
                empSocInfoList.stream()
                        .collect(Collectors.groupingBy(EmpSocInfoDO::getMonthId));
        for (String monthId : monthIdList) {
            // 每项返回值
            EmpSocSmallProgramVO empSocSmallProgramVO = new EmpSocSmallProgramVO();
            EmpSocMonthDO empSocMonth = monthMap.get(monthId);
            empSocSmallProgramVO.setYear(empSocMonth.getYear());
            empSocSmallProgramVO.setMonth(empSocMonth.getMonth());
            // 获取每个月的参保记录
            List<EmpSocInfoDO> empSocInfoGroupMonth = monthIdEmpSocInfoListMap.get(monthId);
            List<String> empSocInfoIdList = empSocInfoGroupMonth.stream()
                    .map(EmpSocInfoDO::getId).collect(Collectors.toList());
            List<EmpSocItemDO> empSocItemList = empSocItemService
                    .selectByEmpSocIdList(empSocInfoIdList, tokenUser.getSiteId());
            Map<String, List<EmpSocItemDO>> groupItem = empSocItemList.stream()
                    .collect(Collectors.groupingBy(EmpSocItemDO::getEmpSocId));
            Map<String, BigDecimal> socItemInfo = new HashMap<>(32);
            Map<String, BigDecimal> pfItemInfo = new HashMap<>(32);
            for (EmpSocInfoDO empSocInfoDO : empSocInfoGroupMonth) {
                // 获取参保记录的子项
                List<EmpSocItemDO> itemList = groupItem.get(empSocInfoDO.getId());
                if (!CollectionUtils.isEmpty(itemList)) {
                    for (EmpSocItemDO empSocItemDO : itemList) {
                        // 判断是社保类型
                        if (SOC_ITEM_TYPE.equals(empSocItemDO.getItemType()) ||
                                ME_ITEM_TYPE.equals(empSocItemDO.getItemType())) {
                            if (socItemInfo.containsKey(empSocItemDO.getItemName())) {
                                socItemInfo.put(empSocItemDO.getItemName(),
                                        socItemInfo.get(empSocItemDO.getItemName()).add(empSocItemDO.getAmountPersonal()));
                            } else {
                                socItemInfo.put(empSocItemDO.getItemName(), empSocItemDO.getAmountPersonal());
                            }
                            // 公积金类型
                        } else if (PF_ITEM_TYPE.equals(empSocItemDO.getItemType())) {
                            if (pfItemInfo.containsKey(empSocItemDO.getItemName())) {
                                pfItemInfo.put(empSocItemDO.getItemName(),
                                        pfItemInfo.get(empSocItemDO.getItemName()).add(empSocItemDO.getAmountPersonal()));
                            } else {
                                pfItemInfo.put(empSocItemDO.getItemName(), empSocItemDO.getAmountPersonal());
                            }
                        }
                    }
                }
            }
            empSocSmallProgramVO.setSocItemInfo(socItemInfo);
            empSocSmallProgramVO.setPfItemInfo(pfItemInfo);
            empSocSmallProgramList.add(empSocSmallProgramVO);
        }
        return PageKit.startPage(empSocSmallProgramList, pageNo, pageSize);
    }

    private List<EmpSocInfoDO> selectNoHaveMonthInfo(String companyId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andIsNull("monthId")
                .andEqualTo("siteId", siteId);
        return empSocInfoDAO.selectByExample(example);
    }

    private List<EmpSocInfoDO> selectEmpSocInfoBrieflyByEmpId(String empId, String companyId, String siteId) {
        return empSocInfoDAO.selectEmpSocInfoBrieflyByEmpId(empId, companyId, siteId);
    }

    @Override
    public Boolean findEmpHaveInfo(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        return !CollectionUtils.isEmpty(empSocInfoList);
    }

    @Override
    public EmpSocInfoVO findEmpHaveSocInfo(String empId, TokenUser tokenUser) {
        EmpSocRelatedDO empSocRelatedDO = empSocRelatedService.selectByEmpId(empId, tokenUser.getSiteId());
        EmpSocInfoVO result = new EmpSocInfoVO();
        Integer socState = empSocRelatedDO.getSocState();
        Integer meState = empSocRelatedDO.getMeState();
        Integer pfState = empSocRelatedDO.getPfState();
        result.setHaveSoc(!NOT_SOC.getValue().equals(socState) && !STOP_SOC.getValue().equals(socState) &&
                !ATTRITION.getValue().equals(socState) && !WAIT_INCREMENT.getValue().equals(socState));
        result.setHaveMe(!NOT_SOC.getValue().equals(meState) && !STOP_SOC.getValue().equals(meState) &&
                !ATTRITION.getValue().equals(meState) && !WAIT_INCREMENT.getValue().equals(meState));
        result.setHavePf(!NOT_SOC.getValue().equals(pfState) && !STOP_SOC.getValue().equals(pfState) &&
                !ATTRITION.getValue().equals(pfState) && !WAIT_INCREMENT.getValue().equals(pfState));
        return result;
    }

    @Override
    public List<EmpSocInfoDO> selectUpdateFillDifferenceEmp(String companyId, List<String> monthIdList, String socProgId, String siteId) {

        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("companyId", companyId)
                .andIn("payCostType", Arrays.asList(NORMAL_PAY_COST.getValue(), FILL_COLLECT.getValue()))
                .andEqualTo("progId", socProgId)
                .andIn("monthId", monthIdList)
                .andEqualTo("siteId", siteId);
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public EmpSocInfoDO selectEmpSocInfoByEmpIdAndMonthIdNormal(String monthId, String empId, String companyId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("empId", empId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("companyId", companyId)
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectOneByExample(example);
    }

    @Override
    public EmpSocInfoDO selectEmpSocInfoByEmpIdAndMonthIdDifCol(String monthId, String empId, String companyId, String year,
                                                                String month, Integer payCostType, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("empId", empId)
                .andEqualTo("payCostType", payCostType)
                .andEqualTo("companyId", companyId)
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectOneByExample(example);
    }


    @Override
    public void syncFillEmpSocInfo(EmpSocInfoDO empSocInfoDO, String monthId, String year, String month, String companyId, String empId, String payCostType) {
        empSocInfoDO.setMonthId(monthId);
        empSocInfoDO.setProgId(SYNC_DATE);
        empSocInfoDO.setYear(year);
        empSocInfoDO.setMonth(month);
        empSocInfoDO.setEmpSocStatus(NORMAL_SOC.getValue());
        empSocInfoDO.setCompanyId(companyId);
        empSocInfoDO.setEmpId(empId);
        empSocInfoDO.setCompanySocAmount(new BigDecimal("0"));
        empSocInfoDO.setCompanyPfAmount(new BigDecimal("0"));
        empSocInfoDO.setPersonalSocAmount(new BigDecimal("0"));
        empSocInfoDO.setPersonalPfAmount(new BigDecimal("0"));
        empSocInfoDO.setPlanFlag(true);
        empSocInfoDO.setPayCostType(Objects.requireNonNull(SocPayCostType.findByDesc(payCostType)).getValue());
    }

    @Override
    public int selectSocProgEmpCount(String socProgId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and().andEqualTo("valid", true).andEqualTo("progId", socProgId);
        return empSocInfoDAO.selectCountByExample(example);
    }

    @Override
    public List<EmpSocInfoDO> selectByMonthIds(String[] monthIds) {
        List<String> monthIdList = Arrays.asList(monthIds);
        Example example = new Example(EmpSocInfoDO.class);
        example.and().andIn("monthId", monthIdList);
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            return new ArrayList<>();
        }
        return empSocInfoList;
    }

    @Override
    public List<EmpSocInfoDO> selectAllNotConfirmed() {
        String[] yearAndMonth = DateUtils.getYearAndMonth(new Date());
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", false)
                .andEqualTo("year", yearAndMonth[0])
                .andEqualTo("month", yearAndMonth[1]);
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocInfoDO> selectByEmpId(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public EmpSocInfoVO selectEmpSocInfoWithCustomerDetails(Integer yearly, Integer monthly, String empId,
                                                            String companyId, TokenUser tokenUser) {

        EmpSocInfoVO empSocInfoVO = initEmpSocInfoVO();
        String monthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId, yearly + ""
                , monthly > 9 ? monthly + "" : "0" + monthly, tokenUser.getSiteId());
        if (!StringUtils.isEmpty(monthId)) {
            List<EmpSocInfoDO> empSocInfoList =
                    selectEmpSocInfoByMonthIdAndEmpId(empId, Collections.singletonList(monthId), companyId, true, tokenUser.getSiteId());
            for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
                empSocInfoVO.setPersonalSocAmount(empSocInfoVO.getPersonalSocAmount().add(empSocInfoDO.getPersonalSocAmount()));
                empSocInfoVO.setPersonalPfAmount(empSocInfoVO.getPersonalPfAmount().add(empSocInfoDO.getPersonalPfAmount()));
                empSocInfoVO.setCompanySocAmount(empSocInfoVO.getCompanySocAmount().add(empSocInfoDO.getCompanySocAmount()));
                empSocInfoVO.setCompanyPfAmount(empSocInfoVO.getCompanyPfAmount().add(empSocInfoDO.getCompanyPfAmount()));
                empSocInfoVO.setServiceCharge(empSocInfoDO.getServiceCost());
            }
        }
        return empSocInfoVO;
    }

    @Override
    public List<EmpSocInfoVO> selectEmpSocInfoWithCustomerDetails(Integer yearly, Integer monthly,
                                                                  List<String> companyIdList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(companyIdList)) {
            return new ArrayList<>();
        }
        List<EmpSocInfoVO> resultList = new ArrayList<>();
        List<String> monthIdList = empSocMonthService.selectMonthIdByYearAndMonthCompanyIdList(companyIdList,
                yearly + "", monthly > 9 ? monthly + "" : "0" + monthly, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(monthIdList)) {
            return new ArrayList<>();
        }
        List<EmpSocInfoDO> empSocInfoList = selectInfoByMonthIdList(monthIdList, tokenUser.getSiteId());
        Map<String, List<EmpSocInfoDO>> empSocInfoMap = empSocInfoList.stream()
                .collect(Collectors.groupingBy(EmpSocInfoDO::getEmpId));
        List<EmpInfoDO> empInfoDOList = empInfoService.selectDoByIdsIgnoreValid(new ArrayList<>(empSocInfoMap.keySet()));
        // 过滤掉非本用工单位员工
        empInfoDOList = empInfoDOList.parallelStream().filter(empInfoDO
                -> companyIdList.contains(empInfoDO.getCompanyId())).collect(Collectors.toList());
        Map<String, String> empIdNameMap = empInfoDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, EmpInfoDO::getName));
        // 差额费用记录
        List<SocDifferenceDO> socDifferenceList = socDifferenceService
                .selectByMonthAndCompanyList(companyIdList, monthIdList, tokenUser.getSiteId());
        Map<String, List<SocDifferenceDO>> groupDifference = socDifferenceList.stream()
                .collect(Collectors.groupingBy(SocDifferenceDO::getEmpId));
        for (String empId : empSocInfoMap.keySet()) {
            EmpSocInfoVO empSocInfoVO = initEmpSocInfoVO();
            empSocInfoVO.setEmpId(empId);
            String empName = empIdNameMap.get(empId);
            if (StringUtils.isEmpty(empName)) {
                continue;
            }
            List<SocDifferenceDO> empDifferenceList = groupDifference.get(empId);
            empSocInfoVO.setEmpName(empName);
            List<EmpSocInfoDO> infoList = empSocInfoMap.get(empId);
            empSocInfoVO.setCompanyId(infoList.get(0).getCompanyId());
            SocProgDO socProgDO = socProgService.selectDOById(infoList.get(0).getProgId(), tokenUser.getSiteId());
            if (ObjectUtils.isEmpty(socProgDO)) {
                EmpSocRelatedDO empSocRelatedDO = empSocRelatedService.selectByEmpId(empId, tokenUser.getSiteId());
                if (empSocRelatedDO != null) {
                    JSONObject empSocRelatedJson = JSONObject.parseObject(empSocRelatedDO.getImportJson());
                    if (!ObjectUtils.isEmpty(empSocRelatedJson)) {
                        empSocInfoVO.setSocCityName(empSocRelatedJson.getString("insuredCity"));
                    }
                }
            } else {
                empSocInfoVO.setSocCityName(socProgDO.getSocCityName());
                empSocInfoVO.setSocCity(socProgDO.getSocCity());
            }
            for (EmpSocInfoDO empSocInfoDO : infoList) {
                empSocInfoVO.setPersonalSocAmount(empSocInfoVO.getPersonalSocAmount().add(empSocInfoDO.getPersonalSocAmount()));
                empSocInfoVO.setPersonalPfAmount(empSocInfoVO.getPersonalPfAmount().add(empSocInfoDO.getPersonalPfAmount()));
                empSocInfoVO.setCompanySocAmount(empSocInfoVO.getCompanySocAmount().add(empSocInfoDO.getCompanySocAmount()));
                empSocInfoVO.setCompanyPfAmount(empSocInfoVO.getCompanyPfAmount().add(empSocInfoDO.getCompanyPfAmount()));
                if (empSocInfoDO.getServiceCost() != null) {
                    empSocInfoVO.setServiceCharge(empSocInfoVO.getServiceCharge().add(empSocInfoDO.getServiceCost()));
                }
                if (empSocInfoDO.getAccumulationFundFine() != null) {
                    empSocInfoVO.setAccumulationFundFine(empSocInfoVO.getAccumulationFundFine()
                            .add(empSocInfoDO.getAccumulationFundFine()));
                }
                if (empSocInfoDO.getSocialSecurityFine() != null) {
                    empSocInfoVO.setSocialSecurityFine(empSocInfoVO.getSocialSecurityFine()
                            .add(empSocInfoDO.getSocialSecurityFine()));
                }
            }
            if(!CollectionUtils.isEmpty(empDifferenceList)){
                for (SocDifferenceDO socDifferenceDO : empDifferenceList) {
                    empSocInfoVO.setPersonalSocAmount(empSocInfoVO.getPersonalSocAmount().add(socDifferenceDO.getPersonalDifference()));
                    empSocInfoVO.setCompanySocAmount(empSocInfoVO.getCompanySocAmount().add(socDifferenceDO.getCompanyDifference()));
                }
            }
            resultList.add(empSocInfoVO);
        }
        return resultList;
    }

    private List<EmpSocInfoDO> selectInfoByMonthIdList(List<String> monthIdList, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andIn("monthId", monthIdList)
                .andEqualTo("siteId", siteId)
                .andEqualTo("valid", true);
        List<EmpSocInfoDO> empSocInfoDOList = empSocInfoDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocInfoDOList)) {
            return new ArrayList<>();
        }
        return empSocInfoDOList.stream()
                .sorted(Comparator.comparing(EmpSocInfoDO::getGmtCreate).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public List<EmpSocInfoDO> selectByIdList(List<String> idList, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIn("id", idList)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocInfoVO> selectByIdListAndSetIdCard(List<String> idList, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIn("id", idList)
                .andEqualTo("siteId", tokenUser.getSiteId());
        List<EmpSocInfoDO> empSocInfoList = empSocInfoDAO.selectByExample(example);
        List<String> empIdList =
                empSocInfoList.stream().map(EmpSocInfoDO::getEmpId).collect(Collectors.toList());
        List<EmpInfoDO> empInfoList = empInfoService.selectDoByIds(empIdList);
        Map<String, EmpInfoDO> empMap =
                empInfoList.stream().collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
        return empSocInfoList.stream()
                .map(empSocInfoDO -> {
                    EmpSocInfoVO empSocInfoVO = empSocInfoDO.to(EmpSocInfoVO.class);
                    EmpInfoDO empInfoDO = empMap.get(empSocInfoVO.getEmpId());
                    if (!ObjectUtils.isEmpty(empInfoDO)) {
                        empSocInfoVO.setIdCard(empInfoDO.getIdCard());
                        empSocInfoVO.setEmpName(empInfoDO.getName());
                    }
                    return empSocInfoVO;
                }).collect(Collectors.toList());
    }

    @Override
    public Integer deleteById(String id) {
        EmpSocInfoDO empSocInfoDO = new EmpSocInfoDO();
        empSocInfoDO.setId(id);
        empSocItemService.batchDeleteByEmpSocInfoIdList(Collections.singletonList(id));
        return empSocInfoDAO.delete(empSocInfoDO);
    }

    @Override
    public EmpSocInfoDO selectEmpSocInfoNotConfirm(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", false)
                .andEqualTo("empId", empId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectOneByExample(example);
    }

    @Override
    public EmpSocInfoDO selectInfoByMonthId(String empId, String monthId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("empId", empId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectOneByExample(example);
    }

    @Override
    public EmpSocInfoDO selectInfoByYearAndMonth(String empId, String year, String month, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("empId", empId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectOneByExample(example);
    }

    @Override
    public void setPersonStatus(EmpSocInfoListVO empSocInfoListVO, String year, String month, String siteId) {
        if (NORMAL_PAY_COST.getValue().equals(empSocInfoListVO.getPayCostType())) {
            EmpSocPlanDO empSocPlanDO = empSocPlanService.selectEmpLatestPlan(year,
                    month, empSocInfoListVO.getEmpId(), siteId);
            if (ObjectUtils.isEmpty(empSocPlanDO)) {
                empSocInfoListVO.setPersonStatus(NORMAL_SOC_STR);
            } else {
                empSocInfoListVO.setPersonStatus(empSocPlanService.getPlanType(empSocPlanDO));
                empSocInfoListVO.setStatusType(empSocPlanDO.getType());
            }
        } else {
            empSocInfoListVO.setPersonStatus(NORMAL_SOC_STR);
        }
    }

    @Override
    public List<EmpSocInfoDO> selectInfoByMonthAndScheme(String monthId, String schemeId, String year, String month, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("monthId", monthId)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("progId", schemeId);
        List<EmpSocInfoDO> result = empSocInfoDAO.selectByExample(example);
        example.clear();
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("payCostType", FILL_COLLECT.getValue())
                .andEqualTo("progId", schemeId);
        result.addAll(empSocInfoDAO.selectByExample(example));
        return result;
    }

    @Override
    public List<EmpSocInfoListVO> selectAllByMonthId(EmpSocInfoQuery empSocInfoQuery, TokenUser tokenUser) {
        return empSocInfoDAO.pageEmpSocInfoList(empSocInfoQuery, tokenUser.getSiteId(), null);
    }

    @Override
    public void bachSave(List<EmpSocInfoDO> bachSaveList) {
        empSocInfoDAO.insertListAndSetId(bachSaveList);
    }

    @Override
    public HeaderFieldsVO getDynamicHead(HeaderFieldsSocQuery headerFieldsSocQuery, TokenUser tokenUser) {
        HeaderFieldsQuery headerFieldsQuery = headerFieldsSocQuery.to(HeaderFieldsQuery.class);
        HeaderFieldsVO head = adminProviderService.getHead(headerFieldsQuery, tokenUser);
        if (ObjectUtils.isEmpty(head)) {
            return null;
        }
        List<HeaderFieldsListVO> canSee = head.getCanSee();
        if (CollectionUtils.isEmpty(canSee)) {
            return head;
        }
        List<HeaderFieldsListVO> sort = canSee.stream()
                .sorted(Comparator.comparing(HeaderFieldsListVO::getSort).reversed()).collect(Collectors.toList());
        Integer maxSort = sort.get(0).getSort();
        List<SocDifferenceDO> socDifferenceList =
                socDifferenceService.selectByMonthId(headerFieldsSocQuery.getMonthId(), tokenUser);
        // 根据差额费用名称做分组,如果某一种差额费用的所有记录的  个人或者单位金额  都是0 则不展示对应的表头
        Map<String, List<SocDifferenceDO>> differenceNameMap = socDifferenceList
                .stream().collect(Collectors.groupingBy(SocDifferenceDO::getDifferenceName));
        List<String> differenceNameList = socDifferenceList.stream()
                .map(SocDifferenceDO::getDifferenceName).distinct().collect(Collectors.toList());
        List<HeaderFieldsListVO> differenceList = new ArrayList<>();
        for (String differenceName : differenceNameList) {
            List<SocDifferenceDO> differenceListByName = differenceNameMap.get(differenceName);
            List<BigDecimal> companyDifferenceList = differenceListByName.stream()
                    .map(SocDifferenceDO::getCompanyDifference).distinct().collect(Collectors.toList());
            List<BigDecimal> personalDifferenceList = differenceListByName.stream()
                    .map(SocDifferenceDO::getPersonalDifference).distinct().collect(Collectors.toList());
            if (!(companyDifferenceList.size() == 1 && companyDifferenceList.get(0).compareTo(BigDecimal.ZERO) == 0)) {
                maxSort += 1;
                HeaderFieldsListVO headerCompany = new HeaderFieldsListVO();
                headerCompany.setFieldName(differenceName + "(单位)");
                headerCompany.setFieldEnName(differenceName + "(单位)");
                headerCompany.setSort(maxSort);
                headerCompany.setId("fixed");
                differenceList.add(headerCompany);
            }
            if (!(personalDifferenceList.size() == 1 && personalDifferenceList.get(0).compareTo(BigDecimal.ZERO) == 0)) {
                maxSort += 1;
                HeaderFieldsListVO headerPersonal = new HeaderFieldsListVO();
                headerPersonal.setFieldName(differenceName + "(个人)");
                headerPersonal.setFieldEnName(differenceName + "(个人)");
                headerPersonal.setSort(maxSort);
                headerPersonal.setId("fixed");
                differenceList.add(headerPersonal);
            }
        }
        canSee.addAll(canSee.size(), differenceList);
        return head;
    }

    @Override
    public void updateWhetherLeave(List<String> empIdList, Boolean whetherLeave,
                                   List<String> monthIdList, TokenUser tokenUser) {
        if (whetherLeave && CollectionUtils.isEmpty(monthIdList)) {
            return;
        }
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andIn("empId", empIdList)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(monthIdList)) {
            example.and().andIn("monthId", monthIdList);
        }
        EmpSocInfoDO empSocInfo = new EmpSocInfoDO();
        empSocInfo.setWhetherLeave(whetherLeave);
        empSocInfoDAO.updateByExampleSelective(empSocInfo, example);
    }

    @Override
    public List<EmpSocInfoDO> selectNormalInfoByMonthId(String monthId, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("payCostType", NORMAL_PAY_COST.getValue())
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocInfoListVO> getNormalInfoByMonthEmpList(SocPlanDispatchUserQuery query, TokenUser tokenUser) {
        if (!StringUtils.isEmpty(query.getCity())) {
            query.setCityName(adminProviderService.selectAreaById(query.getCity()));
        }
        return empSocInfoDAO.getNormalInfoByMonthEmpList(query, tokenUser.getSiteId());
    }

    @Override
    public List<EmpSocInfoListVO> getInfoByMonthCompanyList(SocPlanDispatchUserQuery query, TokenUser tokenUser) {
        if (!StringUtils.isEmpty(query.getCity())) {
            query.setCityName(adminProviderService.selectAreaById(query.getCity()));
        }
        return empSocInfoDAO.socOverviewList(query, tokenUser.getSiteId());
    }

    @Override
    public List<EmpSocInfoDO> selectByCompanyAndMonthId(String companyId, String monthId, String siteId) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("companyId", companyId)
                .andEqualTo("monthId", monthId)
                .andEqualTo("siteId", siteId);
        return empSocInfoDAO.selectByExample(example);
    }

    /**
     * 初始化一个EmpSocInfoVO,就是金额全赋0
     */
    private EmpSocInfoVO initEmpSocInfoVO() {
        EmpSocInfoVO empSocInfoVO = new EmpSocInfoVO();
        BigDecimal bigDecimal = new BigDecimal("0");
        empSocInfoVO.setServiceCharge(bigDecimal);
        empSocInfoVO.setAccumulationFundFine(bigDecimal);
        empSocInfoVO.setSocialSecurityFine(bigDecimal);
        empSocInfoVO.setCompanySocAmount(bigDecimal);
        empSocInfoVO.setCompanyPfAmount(bigDecimal);
        empSocInfoVO.setPersonalPfAmount(bigDecimal);
        empSocInfoVO.setPersonalSocAmount(bigDecimal);
        return empSocInfoVO;
    }

    /**
     * 创建次月账单的时候校验下个月是否还要创建此人的参保信息,并且把对应的社保状态改成停保或正常参保
     */
    private boolean checkEmpStopSocDate(EmpSocRelatedDO empSocRelatedDO, EmpSocInfoListVO previousInfo, TokenUser tokenUser) {
        Integer socState = previousInfo.getEmpSocStatus();
        Integer meState = previousInfo.getEmpMeStatus();
        Integer pfState = previousInfo.getEmpPfStatus();
        boolean flag = false;
        if (ATTRITION.getValue().equals(socState)) {
            flag = true;
            empSocRelatedDO.setSocState(STOP_SOC.getValue());
        } else if (INCREMENT.getValue().equals(socState)) {
            flag = true;
            empSocRelatedDO.setSocState(NORMAL_SOC.getValue());
        } else if (ObjectUtils.isEmpty(socState)) {
            flag = true;
            socState = 0;
        }
        if (ATTRITION.getValue().equals(meState)) {
            flag = true;
            empSocRelatedDO.setMeState(STOP_SOC.getValue());
        } else if (INCREMENT.getValue().equals(meState)) {
            flag = true;
            empSocRelatedDO.setMeState(NORMAL_SOC.getValue());
        } else if (ObjectUtils.isEmpty(meState)) {
            flag = true;
            meState = 0;
        }
        if (ATTRITION.getValue().equals(pfState)) {
            flag = true;
            empSocRelatedDO.setPfState(STOP_SOC.getValue());
        } else if (INCREMENT.getValue().equals(pfState)) {
            flag = true;
            empSocRelatedDO.setPfState(NORMAL_SOC.getValue());
        } else if (ObjectUtils.isEmpty(pfState)) {
            flag = true;
            pfState = 0;
        }
        if (flag) {
            empSocRelatedService.updateByIdSelective(empSocRelatedDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        List<Integer> filterList = Arrays.asList(EmpSocStatus.NOT_SOC.getValue(),
                EmpSocStatus.ATTRITION.getValue(), EmpSocStatus.STOP_SOC.getValue());
        return filterList.contains(socState) && filterList.contains(meState) && filterList.contains(pfState);
    }

    /**
     * 创建此月账单时,根据上个月的参保记录,生成对应的此月的参保子项列表
     */
    private void createSocItemByNextMonth(List<EmpSocInfoDO> insertList, Map<String, EmpSocRelatedDO> empSocRelatedMap,
                                          Map<String, List<EmpSocItemVO>> itemMap, EmpSocMonthDO empSocMonthDO,
                                          Map<String, SocProgDO> schemeMap, TokenUser tokenUser) {
        Set<String> schemeIdSet = schemeMap.keySet();
        List<SocProgItemDO> schemeItemList = socProgItemService
                .selectBySocProgIds(new ArrayList<>(schemeIdSet), tokenUser);
        Map<String, List<SocProgItemDO>> groupItem = schemeItemList
                .stream().collect(Collectors.groupingBy(SocProgItemDO::getProgId));
        List<EmpSocItemDO> insertItemList = new ArrayList<>();
        for (EmpSocInfoDO insert : insertList) {
            // 拿到这个人的社保相关信息,如果这个人需要停保,点了创建次月账单需要做逻辑判断
            EmpSocRelatedDO empSocRelatedDO = empSocRelatedMap.get(insert.getEmpId());
            List<EmpSocItemDO> currentInsertList = new ArrayList<>();
            boolean b = itemMap.containsKey(insert.getId());
            List<EmpSocItemVO> itemList = itemMap.get(insert.getId());
            SocProgDO socProgDO = schemeMap.get(insert.getProgId());
            // 把上个月的子项创建到这个月来
            for (EmpSocItemVO previousItem : itemList) {
                EmpSocItemDO insertItem = new EmpSocItemDO();
                BeanUtils.copyProperties(previousItem, insertItem);
                if (!StringUtils.isEmpty(insert.getProgId()) && !SYNC_DATE.equals(insert.getProgId())) {
                    // 因为上个月可能补差过,参保方案的金额已经改变过了,如果按照上个月的创建此月的金额是一定错误的
                    List<SocProgItemDO> currentSchemeItemList = groupItem.get(insert.getProgId());
                    Map<String, SocProgItemDO> schemeItemMap = currentSchemeItemList.stream()
                            .collect(Collectors.toMap(SocProgItemDO::getItemName, v -> v));
                    SocProgItemDO socProgItemDO = schemeItemMap.get(insertItem.getItemName());
                    if (!ObjectUtils.isEmpty(socProgItemDO)) {
                        compareItemCardinality(socProgDO, insertItem, socProgItemDO);
                    }
                }
                insertItem.setId(null);
                insertItem.preInsert(tokenUser.getUserId());
                insertItem.setEmpSocId(insert.getId());
                insertItem.setSiteId(tokenUser.getSiteId());
                currentInsertList.add(insertItem);
            }
            insertItemList.addAll(currentInsertList);
            // 最后重新计算一下总金额
            if (ObjectUtils.isEmpty(socProgDO)) {
                calculationAmount(insert, currentInsertList, true, tokenUser);
            } else {
                calculationAmount(insert, currentInsertList, socProgDO.getOnlySocAmount(), tokenUser);
            }
        }
        if (!CollectionUtils.isEmpty(insertItemList)) {
            empSocItemDAO.insertListAndSetId(insertItemList);
        }
    }


    /**
     * 比较子项基数
     */
    private void compareItemCardinality(SocProgDO socProgDO, EmpSocItemDO insertItem, SocProgItemDO socProgItemDO) {
        if (socProgDO.getOnlySocAmount()) {
            // 对比参保方案的这个子项和上个月的这个子项金额
            if (insertItem.getAmountPersonal().compareTo(socProgItemDO.getAmountPersonal()) < 0) {
                insertItem.setAmountPersonal(socProgItemDO.getAmountPersonal());
            }
            if (insertItem.getAmountCompany().compareTo(socProgItemDO.getAmountCompany()) < 0) {
                insertItem.setAmountCompany(socProgItemDO.getAmountCompany());
            }
            if (insertItem.getCardinalityDefault().compareTo(socProgItemDO.getCardinalityDefaultAmount()) < 0) {
                insertItem.setCardinalityDefault(socProgItemDO.getCardinalityDefaultAmount());
            }
        } else {
            if (insertItem.getCardinalityDefault().compareTo(socProgItemDO.getCardinalityDefault()) < 0) {
                insertItem.setAmountPersonal(socProgItemDO.getCardinalityDefault().
                        multiply(socProgItemDO.getPersentPersonal().multiply(new BigDecimal("0.01"))));
                insertItem.setPersentPersonal(socProgItemDO.getPersentPersonal());
                insertItem.setAmountCompany(socProgItemDO.getCardinalityDefault().
                        multiply(socProgItemDO.getPersentCompany().multiply(new BigDecimal("0.01"))));
                insertItem.setPersentCompany(socProgItemDO.getPersentCompany());
                insertItem.setCardinalityDefault(socProgItemDO.getCardinalityDefault());
            }
        }
        // 做尾数数处理
        insertItem.setAmountPersonal(empSocTransformService.mantissaProcessing(insertItem.getAmountPersonal(),
                insertItem.getMantissaProcPersonal()));
        insertItem.setAmountCompany(empSocTransformService.mantissaProcessing(insertItem.getAmountCompany(),
                insertItem.getMantissaProcCompany()));
    }

    /**
     * 检查员工的停保时间,校验当前子项创建次月账单的时候是否需要延续到下一个月
     */
    private Boolean checkStopDateWithItem(EmpSocItemDO insertItem, EmpSocRelatedDO empSocRelatedDO, EmpSocMonthDO empSocMonthDO) {
        boolean whetherSkip = false;
        if (SOC_ITEM_TYPE.equals(insertItem.getItemType())) {
            Date socStopDate = empSocRelatedDO.getSocStopDate();
            if (!ObjectUtils.isEmpty(socStopDate)) {
                String socStopYear = DateUtils.getYear(socStopDate);
                String socStopMonth = DateUtils.getMonth(socStopDate);
                if (socStopYear.equals(empSocMonthDO.getYear()) && socStopMonth.equals(empSocMonthDO.getMonth())) {
                    whetherSkip = true;
                    empSocRelatedDO.setSocState(ATTRITION.getValue());
                }
            }
        } else if (ME_ITEM_TYPE.equals(insertItem.getItemType())) {
            Date meStopDate = empSocRelatedDO.getMeStopDate();
            if (!ObjectUtils.isEmpty(meStopDate)) {
                String meStopYear = DateUtils.getYear(meStopDate);
                String meStopMonth = DateUtils.getMonth(meStopDate);
                if (meStopYear.equals(empSocMonthDO.getYear()) && meStopMonth.equals(empSocMonthDO.getMonth())) {
                    whetherSkip = true;
                    empSocRelatedDO.setMeState(ATTRITION.getValue());
                }
            }
        } else if (PF_ITEM_TYPE.equals(insertItem.getItemType())) {
            Date pfSealedDate = empSocRelatedDO.getPfSealedDate();
            if (!ObjectUtils.isEmpty(pfSealedDate)) {
                String pfSealedYear = DateUtils.getYear(pfSealedDate);
                String pfSealedMonth = DateUtils.getMonth(pfSealedDate);
                if (pfSealedYear.equals(empSocMonthDO.getYear()) && pfSealedMonth.equals(empSocMonthDO.getMonth())) {
                    whetherSkip = true;
                    empSocRelatedDO.setPfState(ATTRITION.getValue());
                }
            }
        }
        return whetherSkip;
    }

    /**
     * 根据退费类型查询参保月中的退费记录
     */
    private List<EmpSocInfoDO> selectRefundsInfoByType(String monthId, String empId, String companyId,
                                                       Integer refundsType, TokenUser tokenUser) {
        Example example = new Example(EmpSocInfoDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("planFlag", true)
                .andEqualTo("payCostType", REFUNDS.getValue())
                .andEqualTo("monthId", monthId)
                .andEqualTo("companyId", companyId)
                .andEqualTo("empId", empId)
                .andEqualTo("refundsType", refundsType)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return empSocInfoDAO.selectByExample(example);
    }

    /**
     * 创建次月账单时根据前一个月的社保状态生成次月的社保状态(这里的社保状态是指社保医保公积金统称为社保)
     */
    private void checkNextEmpSocStatus(EmpSocInfoDO current, EmpSocInfoListVO before) {
        Integer empSocStatus = before.getEmpSocStatus();
        Integer empMeStatus = before.getEmpMeStatus();
        Integer empPfStatus = before.getEmpPfStatus();
        if (INCREMENT.getValue().equals(empSocStatus)) {
            current.setEmpSocStatus(NORMAL_SOC.getValue());
        } else if (ATTRITION.getValue().equals(empSocStatus)) {
            current.setEmpSocStatus(STOP_SOC.getValue());
        }
        if (INCREMENT.getValue().equals(empMeStatus)) {
            current.setEmpMeStatus(NORMAL_SOC.getValue());
        } else if (ATTRITION.getValue().equals(empMeStatus)) {
            current.setEmpMeStatus(STOP_SOC.getValue());
        }
        if (INCREMENT.getValue().equals(empPfStatus)) {
            current.setEmpPfStatus(NORMAL_SOC.getValue());
        } else if (ATTRITION.getValue().equals(empPfStatus)) {
            current.setEmpPfStatus(STOP_SOC.getValue());
        }
    }

    /**
     * 查询参保月列表时额外统计信息计算
     */
    private void monthListStatistics(EmpSocInfoListVO monthListVO, EmpSocInfoQuery empSocInfoQuery, String siteId) {
        empSocInfoQuery.setMonthId(monthListVO.getId());
        List<EmpSocInfoListVO> empSocInfoList = empSocInfoDAO.empSocInfoCount(empSocInfoQuery, siteId);
        BigDecimal socAmount = new BigDecimal("0");
        BigDecimal pfAmount = new BigDecimal("0");
        Integer monthIncrement = 0;
        Integer monthStop = 0;
        // 在保人数,正常参保+增员,如果一个人既有正常参保和增员算一人
        Integer monthNormal = 0;
        List<String> inInsuranceEmp = new ArrayList<>();
        List<String> increaseEmp = new ArrayList<>();
        List<String> decreaseEmp = new ArrayList<>();
        for (EmpSocInfoListVO empSocInfo : empSocInfoList) {
            socAmount = socAmount.add(empSocInfo.getPersonalSocAmount()).add(empSocInfo.getCompanySocAmount());
            pfAmount = pfAmount.add(empSocInfo.getPersonalPfAmount()).add(empSocInfo.getCompanyPfAmount());
            List<Integer> statusList = Arrays.asList(empSocInfo.getEmpSocStatus(),
                    empSocInfo.getEmpMeStatus(), empSocInfo.getEmpPfStatus());
            if (statusList.contains(NORMAL_SOC.getValue()) || statusList.contains(INCREMENT.getValue())) {
                if (!inInsuranceEmp.contains(empSocInfo.getEmpId())) {
                    monthNormal++;
                    inInsuranceEmp.add(empSocInfo.getEmpId());
                }
            }
            if (statusList.contains(INCREMENT.getValue())) {
                if (!increaseEmp.contains(empSocInfo.getEmpId())) {
                    monthIncrement++;
                    increaseEmp.add(empSocInfo.getEmpId());
                }
            }
            if (statusList.contains(ATTRITION.getValue())) {
                if (!decreaseEmp.contains(empSocInfo.getEmpId())) {
                    monthStop++;
                    decreaseEmp.add(empSocInfo.getEmpId());
                }
            }
        }
        // 在保人数(增员+正常参保)
        monthListVO.setEmpSocInfoCount(monthNormal);
        // 社保缴费
        monthListVO.setSocAmount(socAmount);
        // 公积金缴费
        monthListVO.setPfAmount(pfAmount);
        // 增员人数
        monthListVO.setMonthIncrement(monthIncrement);
        // 减员人数
        monthListVO.setMonthStop(monthStop);
    }
}




